Collect low-hanging fruits
[wmaker-crm.git] / util / wmagnify.c
blobfb348885522274e68b48483e94e66bf6ea972c08
1 /*
2 * magnify - a X utility for magnifying screen image
4 * 2000/5/21 Alfredo K. Kojima
6 * This program is in the Public Domain.
7 */
9 #include <X11/Xproto.h>
11 #include <WINGs/WINGs.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <stdio.h>
17 * TODO:
18 * - lens that shows where it's magnifying
23 int refreshrate = 200;
25 typedef struct {
26 Drawable d;
27 XRectangle *rects;
28 int rectP;
29 unsigned long lastpixel;
30 unsigned long *buffer;
31 int width, height;
32 int rwidth, rheight; /* size of window in real pixels */
33 int magfactor;
34 int refreshrate;
36 WMWindow *win;
37 WMLabel *label;
38 WMPixmap *pixmap;
40 WMWindow *dlg;
42 WMSlider *speed;
43 WMSlider *magnify;
44 WMButton *okB;
45 WMButton *cancelB;
46 WMButton *newB;
48 int x, y;
49 Bool frozen;
50 Bool firstDraw;
51 Bool markPointerHotspot;
53 WMHandlerID tid;
54 } BufferData;
56 static BufferData *newWindow(int magfactor);
58 int windowCount = 0;
60 int rectBufferSize = 32;
61 Display *dpy, *vdpy;
62 WMScreen *scr;
63 unsigned int black;
64 WMColor *cursorColor1;
65 WMColor *cursorColor2;
67 #ifndef __GNUC__
68 #define inline
69 #endif
71 static BufferData *makeBufferData(WMWindow * win, WMLabel * label, int width, int height, int magfactor)
73 BufferData *data;
75 data = wmalloc(sizeof(BufferData));
77 data->rwidth = width;
78 data->rheight = height;
80 data->refreshrate = refreshrate;
82 data->firstDraw = True;
84 data->magfactor = magfactor;
86 data->rects = wmalloc(sizeof(XRectangle) * rectBufferSize);
87 data->rectP = 0;
89 data->win = win;
90 data->label = label;
92 data->pixmap = WMCreatePixmap(scr, width, height, WMScreenDepth(scr), False);
93 WMSetLabelImage(data->label, data->pixmap);
95 data->d = WMGetPixmapXID(data->pixmap);
97 data->frozen = False;
99 width /= magfactor;
100 height /= magfactor;
101 data->buffer = wmalloc(sizeof(unsigned long) * width * height);
102 memset(data->buffer, 0, width * height * sizeof(unsigned long));
103 data->width = width;
104 data->height = height;
106 return data;
109 static void resizeBufferData(BufferData * data, int width, int height, int magfactor)
111 int w = width / magfactor;
112 int h = height / magfactor;
114 data->rwidth = width;
115 data->rheight = height;
116 data->firstDraw = True;
117 data->magfactor = magfactor;
118 data->buffer = wrealloc(data->buffer, sizeof(unsigned long) * w * h);
119 data->width = w;
120 data->height = h;
121 memset(data->buffer, 0, w * h * sizeof(unsigned long));
123 WMResizeWidget(data->label, width, height);
125 WMReleasePixmap(data->pixmap);
126 data->pixmap = WMCreatePixmap(scr, width, height, WMScreenDepth(scr), False);
127 WMSetLabelImage(data->label, data->pixmap);
129 data->d = WMGetPixmapXID(data->pixmap);
132 static int drawpoint(BufferData * data, unsigned long pixel, int x, int y)
134 static GC gc = NULL;
135 Bool flush = (x < 0);
137 if (!flush) {
138 if (data->buffer[x + data->width * y] == pixel && !data->firstDraw)
139 return 0;
141 data->buffer[x + data->width * y] = pixel;
143 if (gc == NULL) {
144 gc = XCreateGC(dpy, DefaultRootWindow(dpy), 0, NULL);
147 if (!flush && data->lastpixel == pixel && data->rectP < rectBufferSize) {
148 data->rects[data->rectP].x = x * data->magfactor;
149 data->rects[data->rectP].y = y * data->magfactor;
150 data->rects[data->rectP].width = data->magfactor;
151 data->rects[data->rectP].height = data->magfactor;
152 data->rectP++;
154 return 0;
156 XSetForeground(dpy, gc, data->lastpixel);
157 XFillRectangles(dpy, data->d, gc, data->rects, data->rectP);
158 data->rectP = 0;
159 data->rects[data->rectP].x = x * data->magfactor;
160 data->rects[data->rectP].y = y * data->magfactor;
161 data->rects[data->rectP].width = data->magfactor;
162 data->rects[data->rectP].height = data->magfactor;
163 data->rectP++;
165 data->lastpixel = pixel;
167 return 1;
170 static inline unsigned long getpix(XImage * image, int x, int y, int xoffs, int yoffs)
172 if (x < xoffs || y < yoffs || x >= xoffs + image->width || y >= yoffs + image->height) {
173 return black;
175 return XGetPixel(image, x - xoffs, y - yoffs);
178 static void updateImage(BufferData * data, int rx, int ry)
180 int gx, gy, gw, gh;
181 int x, y;
182 int xoffs, yoffs;
183 int changedPixels = 0;
184 XImage *image;
186 gw = data->width;
187 gh = data->height;
189 gx = rx - gw / 2;
190 gy = ry - gh / 2;
192 xoffs = yoffs = 0;
193 if (gx < 0) {
194 xoffs = abs(gx);
195 gw += gx;
196 gx = 0;
198 if (gx + gw >= WidthOfScreen(DefaultScreenOfDisplay(vdpy))) {
199 gw = WidthOfScreen(DefaultScreenOfDisplay(vdpy)) - gx;
201 if (gy < 0) {
202 yoffs = abs(gy);
203 gh += gy;
204 gy = 0;
206 if (gy + gh >= HeightOfScreen(DefaultScreenOfDisplay(vdpy))) {
207 gh = HeightOfScreen(DefaultScreenOfDisplay(vdpy)) - gy;
210 image = XGetImage(vdpy, DefaultRootWindow(vdpy), gx, gy, gw, gh, AllPlanes, ZPixmap);
212 for (y = 0; y < data->height; y++) {
213 for (x = 0; x < data->width; x++) {
214 unsigned long pixel;
216 pixel = getpix(image, x, y, xoffs, yoffs);
218 if (drawpoint(data, pixel, x, y))
219 changedPixels++;
222 /* flush the point cache */
223 drawpoint(data, 0, -1, -1);
225 XDestroyImage(image);
227 if (data->markPointerHotspot && !data->frozen) {
228 XRectangle rects[4];
230 rects[0].x = (data->width / 2 - 3) * data->magfactor;
231 rects[0].y = (data->height / 2) * data->magfactor;
232 rects[0].width = 2 * data->magfactor;
233 rects[0].height = data->magfactor;
235 rects[1].x = (data->width / 2 + 2) * data->magfactor;
236 rects[1].y = (data->height / 2) * data->magfactor;
237 rects[1].width = 2 * data->magfactor;
238 rects[1].height = data->magfactor;
240 XFillRectangles(dpy, data->d, WMColorGC(cursorColor1), rects, 2);
242 rects[2].y = (data->height / 2 - 3) * data->magfactor;
243 rects[2].x = (data->width / 2) * data->magfactor;
244 rects[2].height = 2 * data->magfactor;
245 rects[2].width = data->magfactor;
247 rects[3].y = (data->height / 2 + 2) * data->magfactor;
248 rects[3].x = (data->width / 2) * data->magfactor;
249 rects[3].height = 2 * data->magfactor;
250 rects[3].width = data->magfactor;
252 XFillRectangles(dpy, data->d, WMColorGC(cursorColor2), rects + 2, 2);
255 if (changedPixels > 0) {
256 WMRedisplayWidget(data->label);
259 data->firstDraw = False;
262 static void update(void *d)
264 BufferData *data = (BufferData *) d;
265 Window win;
266 int rx, ry;
267 int bla;
268 unsigned ubla;
270 if (data->frozen) {
271 rx = data->x;
272 ry = data->y;
273 } else {
274 XQueryPointer(dpy, DefaultRootWindow(dpy), &win, &win, &rx, &ry, &bla, &bla, &ubla);
276 updateImage(data, rx, ry);
278 data->tid = WMAddTimerHandler(data->refreshrate, update, data);
281 void resizedWindow(void *d, WMNotification * notif)
283 BufferData *data = (BufferData *) d;
284 WMView *view = (WMView *) WMGetNotificationObject(notif);
285 WMSize size;
287 size = WMGetViewSize(view);
289 resizeBufferData(data, size.width, size.height, data->magfactor);
292 void closeWindow(WMWidget * w, void *d)
294 BufferData *data = (BufferData *) d;
296 windowCount--;
297 if (windowCount == 0) {
298 exit(0);
299 } else {
300 WMDeleteTimerHandler(data->tid);
301 WMDestroyWidget(w);
302 wfree(data->buffer);
303 wfree(data->rects);
304 WMReleasePixmap(data->pixmap);
305 wfree(data);
309 static void keyHandler(XEvent * event, void *d)
311 BufferData *data = (BufferData *) d;
312 char buf[32];
313 KeySym ks;
314 WMView *view = WMWidgetView(data->win);
315 WMSize size;
317 size = WMGetViewSize(view);
319 if (XLookupString(&event->xkey, buf, 31, &ks, NULL) > 0) {
320 switch (buf[0]) {
321 case 'n':
322 newWindow(data->magfactor);
323 break;
324 case 'm':
325 data->markPointerHotspot = !data->markPointerHotspot;
326 break;
327 case 'f':
328 case ' ':
329 data->frozen = !data->frozen;
330 if (data->frozen) {
331 data->x = event->xkey.x_root;
332 data->y = event->xkey.y_root;
333 sprintf(buf, "[Magnify %ix]", data->magfactor);
334 } else {
335 sprintf(buf, "Magnify %ix", data->magfactor);
337 WMSetWindowTitle(data->win, buf);
338 break;
339 case '1':
340 case '2':
341 case '3':
342 case '4':
343 case '5':
344 case '6':
345 case '7':
346 case '8':
347 case '9':
348 resizeBufferData(data, size.width, size.height, buf[0] - '0');
349 if (data->frozen) {
350 sprintf(buf, "[Magnify %ix]", data->magfactor);
351 } else {
352 sprintf(buf, "Magnify %ix", data->magfactor);
354 WMSetWindowTitle(data->win, buf);
355 break;
360 static BufferData *newWindow(int magfactor)
362 WMWindow *win;
363 WMLabel *label;
364 BufferData *data;
365 char buf[32];
367 windowCount++;
369 win = WMCreateWindow(scr, "magnify");
370 WMResizeWidget(win, 300, 200);
371 sprintf(buf, "Magnify %ix", magfactor);
372 WMSetWindowTitle(win, buf);
373 WMSetViewNotifySizeChanges(WMWidgetView(win), True);
375 label = WMCreateLabel(win);
376 WMResizeWidget(label, 300, 200);
377 WMMoveWidget(label, 0, 0);
378 WMSetLabelRelief(label, WRSunken);
379 WMSetLabelImagePosition(label, WIPImageOnly);
381 data = makeBufferData(win, label, 300, 200, magfactor);
383 WMCreateEventHandler(WMWidgetView(win), KeyReleaseMask, keyHandler, data);
385 WMAddNotificationObserver(resizedWindow, data, WMViewSizeDidChangeNotification, WMWidgetView(win));
387 WMRealizeWidget(win);
389 WMMapSubwidgets(win);
390 WMMapWidget(win);
392 WMSetWindowCloseAction(win, closeWindow, data);
393 data->tid = WMAddTimerHandler(refreshrate, update, data);
395 return data;
398 int main(int argc, char **argv)
400 BufferData *data;
401 int i;
402 char *display = "";
403 char *vdisplay = NULL;
404 int magfactor = 2;
406 WMInitializeApplication("Magnify", &argc, argv);
408 for (i = 1; i < argc; i++) {
409 if (strcmp(argv[i], "-display") == 0) {
410 i++;
411 if (i >= argc)
412 goto help;
413 display = argv[i];
414 } else if (strcmp(argv[i], "-vdisplay") == 0) {
415 i++;
416 if (i >= argc)
417 goto help;
418 vdisplay = argv[i];
419 } else if (strcmp(argv[i], "-m") == 0) {
420 i++;
421 if (i >= argc)
422 goto help;
423 magfactor = atoi(argv[i]);
424 if (magfactor < 1 || magfactor > 32) {
425 printf("%s:invalid magnification factor ``%s''\n", argv[0], argv[i]);
426 exit(1);
428 } else if (strcmp(argv[i], "-r") == 0) {
429 i++;
430 if (i >= argc)
431 goto help;
432 refreshrate = atoi(argv[i]);
433 if (refreshrate < 1) {
434 printf("%s:invalid refresh rate ``%s''\n", argv[0], argv[i]);
435 exit(1);
437 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
438 help:
440 printf("Syntax: %s [options]\n", argv[0]);
441 puts("Options:");
442 puts(" -display <display> display that should be used");
443 puts(" -m <number> change magnification factor (default 2)");
444 puts(" -r <number> change refresh delay, in milliseconds (default 200)");
445 puts("Keys:");
446 puts(" 1,2,3,4,5,6,7,8,9 change the magnification factor");
447 puts(" <space>, f freeze the 'camera', making it magnify only the current\n"
448 " position");
449 puts(" n create a new window");
450 puts(" m show/hide the pointer hotspot mark");
451 exit(0);
455 dpy = XOpenDisplay(display);
456 if (!dpy) {
457 puts("couldnt open display");
458 exit(1);
461 if (vdisplay) {
462 vdpy = XOpenDisplay(vdisplay);
463 if (!vdpy) {
464 puts("couldnt open display to be viewed");
465 exit(1);
467 } else {
468 vdpy = dpy;
471 /* calculate how many rectangles we can send in a trip to the server */
472 rectBufferSize = XMaxRequestSize(dpy) - 128;
473 rectBufferSize /= sizeof(XRectangle);
474 if (rectBufferSize < 1)
475 rectBufferSize = 1;
477 black = BlackPixel(dpy, DefaultScreen(dpy));
479 scr = WMCreateScreen(dpy, 0);
481 cursorColor1 = WMCreateNamedColor(scr, "#ff0000", False);
482 cursorColor2 = WMCreateNamedColor(scr, "#00ff00", False);
484 data = newWindow(magfactor);
486 WMScreenMainLoop(scr);
488 return 0;