1 /*
2  * magnify - a X utility for magnifying screen image
3  *
4  * 2000/5/21  Alfredo K. Kojima
5  *
6  * This program is in the Public Domain.
7  */
8 #include <config.h>
9 
10 #include <X11/Xproto.h>
11 
12 #include <WINGs/WINGs.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <stdio.h>
16 
17 /*
18  * TODO:
19  * - lens that shows where it's magnifying
20  *
21  *
22  */
23 
24 int refreshrate = 200;
25 
26 typedef struct {
27 	Drawable d;
28 	XRectangle *rects;
29 	int rectP;
30 	unsigned long lastpixel;
31 	unsigned long *buffer;
32 	int width, height;
33 	int rwidth, rheight;	/* size of window in real pixels */
34 	int magfactor;
35 	int refreshrate;
36 
37 	WMWindow *win;
38 	WMLabel *label;
39 	WMPixmap *pixmap;
40 
41 	WMWindow *dlg;
42 
43 	WMSlider *speed;
44 	WMSlider *magnify;
45 	WMButton *okB;
46 	WMButton *cancelB;
47 	WMButton *newB;
48 
49 	int x, y;
50 	Bool frozen;
51 	Bool firstDraw;
52 	Bool markPointerHotspot;
53 
54 	WMHandlerID tid;
55 } BufferData;
56 
57 static BufferData *newWindow(int magfactor);
58 
59 int windowCount = 0;
60 
61 int rectBufferSize = 32;
62 Display *dpy, *vdpy;
63 WMScreen *scr;
64 unsigned int black;
65 WMColor *cursorColor1;
66 WMColor *cursorColor2;
67 
68 
makeBufferData(WMWindow * win,WMLabel * label,int width,int height,int magfactor)69 static BufferData *makeBufferData(WMWindow * win, WMLabel * label, int width, int height, int magfactor)
70 {
71 	BufferData *data;
72 
73 	data = wmalloc(sizeof(BufferData));
74 
75 	data->rwidth = width;
76 	data->rheight = height;
77 
78 	data->refreshrate = refreshrate;
79 
80 	data->firstDraw = True;
81 
82 	data->magfactor = magfactor;
83 
84 	data->rects = wmalloc(sizeof(XRectangle) * rectBufferSize);
85 	data->rectP = 0;
86 
87 	data->win = win;
88 	data->label = label;
89 
90 	data->pixmap = WMCreatePixmap(scr, width, height, WMScreenDepth(scr), False);
91 	WMSetLabelImage(data->label, data->pixmap);
92 
93 	data->d = WMGetPixmapXID(data->pixmap);
94 
95 	data->frozen = False;
96 
97 	width /= magfactor;
98 	height /= magfactor;
99 	data->buffer = wmalloc(sizeof(unsigned long) * width * height);
100 	data->width = width;
101 	data->height = height;
102 
103 	return data;
104 }
105 
resizeBufferData(BufferData * data,int width,int height,int magfactor)106 static void resizeBufferData(BufferData * data, int width, int height, int magfactor)
107 {
108 	int w = width / magfactor;
109 	int h = height / magfactor;
110 
111 	data->rwidth = width;
112 	data->rheight = height;
113 	data->firstDraw = True;
114 	data->magfactor = magfactor;
115 	data->buffer = wrealloc(data->buffer, sizeof(unsigned long) * w * h);
116 	data->width = w;
117 	data->height = h;
118 	memset(data->buffer, 0, w * h * sizeof(unsigned long));
119 
120 	WMResizeWidget(data->label, width, height);
121 
122 	WMReleasePixmap(data->pixmap);
123 	data->pixmap = WMCreatePixmap(scr, width, height, WMScreenDepth(scr), False);
124 	WMSetLabelImage(data->label, data->pixmap);
125 
126 	data->d = WMGetPixmapXID(data->pixmap);
127 }
128 
drawpoint(BufferData * data,unsigned long pixel,int x,int y)129 static int drawpoint(BufferData * data, unsigned long pixel, int x, int y)
130 {
131 	static GC gc = NULL;
132 	Bool flush = (x < 0);
133 
134 	if (!flush) {
135 		if (data->buffer[x + data->width * y] == pixel && !data->firstDraw)
136 			return 0;
137 
138 		data->buffer[x + data->width * y] = pixel;
139 	}
140 	if (gc == NULL) {
141 		gc = XCreateGC(dpy, DefaultRootWindow(dpy), 0, NULL);
142 	}
143 
144 	if (!flush && data->lastpixel == pixel && data->rectP < rectBufferSize) {
145 		data->rects[data->rectP].x = x * data->magfactor;
146 		data->rects[data->rectP].y = y * data->magfactor;
147 		data->rects[data->rectP].width = data->magfactor;
148 		data->rects[data->rectP].height = data->magfactor;
149 		data->rectP++;
150 
151 		return 0;
152 	}
153 	XSetForeground(dpy, gc, data->lastpixel);
154 	XFillRectangles(dpy, data->d, gc, data->rects, data->rectP);
155 	data->rectP = 0;
156 	data->rects[data->rectP].x = x * data->magfactor;
157 	data->rects[data->rectP].y = y * data->magfactor;
158 	data->rects[data->rectP].width = data->magfactor;
159 	data->rects[data->rectP].height = data->magfactor;
160 	data->rectP++;
161 
162 	data->lastpixel = pixel;
163 
164 	return 1;
165 }
166 
getpix(XImage * image,int x,int y,int xoffs,int yoffs)167 static inline unsigned long getpix(XImage * image, int x, int y, int xoffs, int yoffs)
168 {
169 	if (x < xoffs || y < yoffs || x >= xoffs + image->width || y >= yoffs + image->height) {
170 		return black;
171 	}
172 	return XGetPixel(image, x - xoffs, y - yoffs);
173 }
174 
updateImage(BufferData * data,int rx,int ry)175 static void updateImage(BufferData * data, int rx, int ry)
176 {
177 	int gx, gy, gw, gh;
178 	int x, y;
179 	int xoffs, yoffs;
180 	int changedPixels = 0;
181 	XImage *image;
182 
183 	gw = data->width;
184 	gh = data->height;
185 
186 	gx = rx - gw / 2;
187 	gy = ry - gh / 2;
188 
189 	xoffs = yoffs = 0;
190 	if (gx < 0) {
191 		xoffs = abs(gx);
192 		gw += gx;
193 		gx = 0;
194 	}
195 	if (gx + gw >= WidthOfScreen(DefaultScreenOfDisplay(vdpy))) {
196 		gw = WidthOfScreen(DefaultScreenOfDisplay(vdpy)) - gx;
197 	}
198 	if (gy < 0) {
199 		yoffs = abs(gy);
200 		gh += gy;
201 		gy = 0;
202 	}
203 	if (gy + gh >= HeightOfScreen(DefaultScreenOfDisplay(vdpy))) {
204 		gh = HeightOfScreen(DefaultScreenOfDisplay(vdpy)) - gy;
205 	}
206 
207 	image = XGetImage(vdpy, DefaultRootWindow(vdpy), gx, gy, gw, gh, AllPlanes, ZPixmap);
208 
209 	for (y = 0; y < data->height; y++) {
210 		for (x = 0; x < data->width; x++) {
211 			unsigned long pixel;
212 
213 			pixel = getpix(image, x, y, xoffs, yoffs);
214 
215 			if (drawpoint(data, pixel, x, y))
216 				changedPixels++;
217 		}
218 	}
219 	/* flush the point cache */
220 	drawpoint(data, 0, -1, -1);
221 
222 	XDestroyImage(image);
223 
224 	if (data->markPointerHotspot && !data->frozen) {
225 		XRectangle rects[4];
226 
227 		rects[0].x = (data->width / 2 - 3) * data->magfactor;
228 		rects[0].y = (data->height / 2) * data->magfactor;
229 		rects[0].width = 2 * data->magfactor;
230 		rects[0].height = data->magfactor;
231 
232 		rects[1].x = (data->width / 2 + 2) * data->magfactor;
233 		rects[1].y = (data->height / 2) * data->magfactor;
234 		rects[1].width = 2 * data->magfactor;
235 		rects[1].height = data->magfactor;
236 
237 		XFillRectangles(dpy, data->d, WMColorGC(cursorColor1), rects, 2);
238 
239 		rects[2].y = (data->height / 2 - 3) * data->magfactor;
240 		rects[2].x = (data->width / 2) * data->magfactor;
241 		rects[2].height = 2 * data->magfactor;
242 		rects[2].width = data->magfactor;
243 
244 		rects[3].y = (data->height / 2 + 2) * data->magfactor;
245 		rects[3].x = (data->width / 2) * data->magfactor;
246 		rects[3].height = 2 * data->magfactor;
247 		rects[3].width = data->magfactor;
248 
249 		XFillRectangles(dpy, data->d, WMColorGC(cursorColor2), rects + 2, 2);
250 	}
251 
252 	if (changedPixels > 0) {
253 		WMRedisplayWidget(data->label);
254 	}
255 
256 	data->firstDraw = False;
257 }
258 
update(void * d)259 static void update(void *d)
260 {
261 	BufferData *data = (BufferData *) d;
262 	Window win;
263 	int rx, ry;
264 	int bla;
265 	unsigned ubla;
266 
267 	if (data->frozen) {
268 		rx = data->x;
269 		ry = data->y;
270 	} else {
271 		XQueryPointer(dpy, DefaultRootWindow(dpy), &win, &win, &rx, &ry, &bla, &bla, &ubla);
272 	}
273 	updateImage(data, rx, ry);
274 
275 	data->tid = WMAddTimerHandler(data->refreshrate, update, data);
276 }
277 
resizedWindow(void * d,WMNotification * notif)278 static void resizedWindow(void *d, WMNotification * notif)
279 {
280 	BufferData *data = (BufferData *) d;
281 	WMView *view = (WMView *) WMGetNotificationObject(notif);
282 	WMSize size;
283 
284 	size = WMGetViewSize(view);
285 
286 	resizeBufferData(data, size.width, size.height, data->magfactor);
287 }
288 
closeWindow(WMWidget * w,void * d)289 static void closeWindow(WMWidget * w, void *d)
290 {
291 	BufferData *data = (BufferData *) d;
292 
293 	windowCount--;
294 	if (windowCount == 0) {
295 		WMReleaseApplication();
296 		exit(0);
297 	} else {
298 		WMDeleteTimerHandler(data->tid);
299 		WMDestroyWidget(w);
300 		wfree(data->buffer);
301 		wfree(data->rects);
302 		WMReleasePixmap(data->pixmap);
303 		wfree(data);
304 	}
305 }
306 
keyHandler(XEvent * event,void * d)307 static void keyHandler(XEvent * event, void *d)
308 {
309 	BufferData *data = (BufferData *) d;
310 	char buf[32];
311 	KeySym ks;
312 	WMView *view = WMWidgetView(data->win);
313 	WMSize size;
314 
315 	size = WMGetViewSize(view);
316 
317 	if (XLookupString(&event->xkey, buf, 31, &ks, NULL) > 0) {
318 		switch (buf[0]) {
319 		case 'n':
320 			newWindow(data->magfactor);
321 			break;
322 		case 'm':
323 			data->markPointerHotspot = !data->markPointerHotspot;
324 			break;
325 		case 'f':
326 		case ' ':
327 			data->frozen = !data->frozen;
328 			if (data->frozen) {
329 				data->x = event->xkey.x_root;
330 				data->y = event->xkey.y_root;
331 				sprintf(buf, "[Magnify %ix]", data->magfactor);
332 			} else {
333 				sprintf(buf, "Magnify %ix", data->magfactor);
334 			}
335 			WMSetWindowTitle(data->win, buf);
336 			break;
337 		case '1':
338 		case '2':
339 		case '3':
340 		case '4':
341 		case '5':
342 		case '6':
343 		case '7':
344 		case '8':
345 		case '9':
346 			resizeBufferData(data, size.width, size.height, buf[0] - '0');
347 			if (data->frozen) {
348 				sprintf(buf, "[Magnify %ix]", data->magfactor);
349 			} else {
350 				sprintf(buf, "Magnify %ix", data->magfactor);
351 			}
352 			WMSetWindowTitle(data->win, buf);
353 			break;
354 		}
355 	}
356 }
357 
newWindow(int magfactor)358 static BufferData *newWindow(int magfactor)
359 {
360 	WMWindow *win;
361 	WMLabel *label;
362 	BufferData *data;
363 	char buf[32];
364 
365 	windowCount++;
366 
367 	win = WMCreateWindow(scr, "magnify");
368 	WMResizeWidget(win, 300, 200);
369 	sprintf(buf, "Magnify %ix", magfactor);
370 	WMSetWindowTitle(win, buf);
371 	WMSetViewNotifySizeChanges(WMWidgetView(win), True);
372 
373 	label = WMCreateLabel(win);
374 	WMResizeWidget(label, 300, 200);
375 	WMMoveWidget(label, 0, 0);
376 	WMSetLabelRelief(label, WRSunken);
377 	WMSetLabelImagePosition(label, WIPImageOnly);
378 
379 	data = makeBufferData(win, label, 300, 200, magfactor);
380 
381 	WMCreateEventHandler(WMWidgetView(win), KeyReleaseMask, keyHandler, data);
382 
383 	WMAddNotificationObserver(resizedWindow, data, WMViewSizeDidChangeNotification, WMWidgetView(win));
384 
385 	WMRealizeWidget(win);
386 
387 	WMMapSubwidgets(win);
388 	WMMapWidget(win);
389 
390 	WMSetWindowCloseAction(win, closeWindow, data);
391 	data->tid = WMAddTimerHandler(refreshrate, update, data);
392 
393 	return data;
394 }
395 
main(int argc,char ** argv)396 int main(int argc, char **argv)
397 {
398 	int i;
399 	char *display = "";
400 	char *vdisplay = NULL;
401 	int magfactor = 2;
402 
403 	WMInitializeApplication("Magnify", &argc, argv);
404 
405 	for (i = 1; i < argc; i++) {
406 		if (strcmp(argv[i], "-display") == 0) {
407 			i++;
408 			if (i >= argc)
409 				goto help;
410 			display = argv[i];
411 		} else if (strcmp(argv[i], "-vdisplay") == 0) {
412 			i++;
413 			if (i >= argc)
414 				goto help;
415 			vdisplay = argv[i];
416 		} else if (strcmp(argv[i], "-m") == 0) {
417 			i++;
418 			if (i >= argc)
419 				goto help;
420 			magfactor = atoi(argv[i]);
421 			if (magfactor < 1 || magfactor > 32) {
422 				printf("%s:invalid magnification factor ``%s''\n", argv[0], argv[i]);
423 				exit(1);
424 			}
425 		} else if (strcmp(argv[i], "-r") == 0) {
426 			i++;
427 			if (i >= argc)
428 				goto help;
429 			refreshrate = atoi(argv[i]);
430 			if (refreshrate < 1) {
431 				printf("%s:invalid refresh rate ``%s''\n", argv[0], argv[i]);
432 				exit(1);
433 			}
434 		} else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
435  help:
436 
437 			printf("Usage: %s [options]\n", argv[0]);
438 			puts("Options:");
439 			puts("  -display <display>\tdisplay where to magnification is shown");
440 			puts("  -m <number>\t\tchange magnification factor (default 2)");
441 			puts("  -r <number>\t\tchange refresh delay, in milliseconds (default 200)");
442 			puts("  -vdisplay <display>\tdisplay from which the magnification is taken");
443 			puts("  -h, --help\t\tdisplay this help page");
444 			puts("Keys:");
445 			puts("  1,2,3,4,5,6,7,8,9	change the magnification factor");
446 			puts("  <space>, f		freeze the 'camera', making it magnify only the current\n"
447 			     "			position");
448 			puts("  n			create a new window");
449 			puts("  m			show/hide the pointer hotspot mark");
450 			exit(0);
451 		}
452 	}
453 
454 	dpy = XOpenDisplay(display);
455 	if (!dpy) {
456 		puts("could not open display");
457 		exit(1);
458 	}
459 
460 	if (vdisplay) {
461 		vdpy = XOpenDisplay(vdisplay);
462 		if (!vdpy) {
463 			puts("could not open display to be viewed");
464 			exit(1);
465 		}
466 	} else {
467 		vdpy = dpy;
468 	}
469 
470 	/* calculate how many rectangles we can send in a trip to the server */
471 	rectBufferSize = XMaxRequestSize(dpy) - 128;
472 	rectBufferSize /= sizeof(XRectangle);
473 	if (rectBufferSize < 1)
474 		rectBufferSize = 1;
475 
476 	black = BlackPixel(dpy, DefaultScreen(dpy));
477 
478 	scr = WMCreateScreen(dpy, 0);
479 
480 	cursorColor1 = WMCreateNamedColor(scr, "#ff0000", False);
481 	cursorColor2 = WMCreateNamedColor(scr, "#00ff00", False);
482 
483 	newWindow(magfactor);
484 
485 	WMScreenMainLoop(scr);
486 
487 	return 0;
488 }
489