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