xzoom

A simple screen magnifier for X11.
git clone git://r-36.net/xzoom
Log | Files | Refs

xzoom.c (20211B)


      1 /* Copyright Itai Nahshon 1995, 1996.
      2    This program is distributed with no warranty.
      3 
      4    Source files for this program may be distributed freely.
      5    Modifications to this file are okay as long as:
      6     a. This copyright notice and comment are preserved and
      7 	   left at the top of the file.
      8 	b. The man page is fixed to reflect the change.
      9 	c. The author of this change adds his name and change
     10 	   description to the list of changes below.
     11    Executable files may be distributed with sources, or with
     12    exact location where the source code can be obtained.
     13 
     14 Changelist:
     15 Author                    Description
     16 ------                    -----------
     17 Itai Nahshon              Version 0.1, Nov. 21 1995
     18 Itai Nahshon              Version 0.2, Apr. 17 1996
     19                           include <sys/types.h>
     20                           Use memmove() instead of memcopy()
     21                           Optional macro to replace call to usleep().
     22 Markus F.X.J. Oberhumer   Version 0.4, Feb. 18 1998
     23                           split into 2 files (scale.h)
     24                           added support for 15, 16, 24 and 32 bpp displays
     25                           added a grid (press key 'g')
     26                           optimized scaling routines
     27                           use memcpy() instead of memmove() ;-)
     28                           some other minor changes/fixes
     29 tony mancill		2002/02/13 <tmancill@debian.org>
     30 			hacked in support for WM_DELETE_WINDOW
     31 */
     32 
     33 #include <stdio.h>
     34 #include <string.h>
     35 #include <stdlib.h>
     36 #include <sys/types.h>
     37 #include <sys/signal.h>
     38 
     39 #include <X11/Xlib.h>
     40 #include <X11/Xatom.h>
     41 #include <X11/Xutil.h>
     42 
     43 #ifdef XSHM
     44 #include <sys/ipc.h>
     45 #include <sys/shm.h>
     46 #include <X11/extensions/XShm.h>
     47 #endif
     48 
     49 #include <X11/cursorfont.h>
     50 #include <X11/keysym.h>
     51 
     52 #ifdef TIMER
     53 #include <sys/time.h>
     54 #include <unistd.h>
     55 #endif
     56 
     57 Display *dpy;
     58 Screen *scr;
     59 Window win;
     60 Atom wm_delete_window;
     61 Atom wm_protocols;
     62 Atom wm_dock;
     63 Atom wm_windowtype;
     64 Status status;
     65 
     66 GC gc;
     67 #ifdef FRAME
     68 GC framegc;
     69 #endif
     70 
     71 #ifdef TIMER
     72 Font font;
     73 struct timeval old_time;
     74 #endif
     75 
     76 Cursor when_button;
     77 Cursor crosshair;
     78 
     79 char *progname;
     80 int set_title;
     81 
     82 #define SRC		0				/* index for source image */
     83 #define	DST		1				/* index for dest image */
     84 
     85 #define WIDTH	256				/* default width */
     86 #define HEIGHT	256				/* default height */
     87 
     88 #define MAG		2				/* default magnification */
     89 #define MAGX	MAG				/* horizontal magnification */
     90 #define MAGY	MAG				/* vertical magnification */
     91 
     92 int xgrab, ygrab;				/* where do we take the picture from */
     93 
     94 int magx = MAGX;
     95 int magy = MAGY;
     96 
     97 int flipxy = False;				/* flip x and y */
     98 int flipx = False;				/* flip display about y axis */
     99 int flipy = False;				/* flip display about x axiz */
    100 
    101 int xzoom_flag = False;			/* next mag change only to magx */
    102 int yzoom_flag = False;			/* next mag change only to magy */
    103 
    104 int gridx = False;
    105 int gridy = False;
    106 
    107 int width[2] = { 0, WIDTH };
    108 int height[2] = { 0, HEIGHT };
    109 unsigned depth = 0;
    110 
    111 #ifdef XSHM
    112 XShmSegmentInfo shminfo[2];			/* Segment info.  */
    113 #endif
    114 XImage *ximage[2];					/* Ximage struct. */
    115 
    116 int created_images = False;
    117 
    118 #define NDELAYS 5
    119 
    120 int delays[NDELAYS] = { 200000, 100000, 50000, 10000, 0 };
    121 int delay_index = 0;
    122 int delay = 200000;			/* 0.2 second between updates */
    123 
    124 void
    125 timeout_func(int signum) {
    126 	set_title = True;
    127 	signum = signum;          /* UNUSED */
    128 }
    129 
    130 void
    131 allocate_images(void) {
    132 	int i;
    133 
    134 	for(i = 0; i < 2; i++) {
    135 
    136 #ifdef XSHM
    137 		ximage[i] = XShmCreateImage(dpy,
    138 			DefaultVisualOfScreen(scr),
    139 			DefaultDepthOfScreen(scr),
    140 			ZPixmap, NULL, &shminfo[i],
    141 			width[i], height[i]);
    142 
    143 		if(ximage[i] == NULL) {
    144 			perror("XShmCreateImage");
    145 			exit(-1);
    146 		}
    147 
    148 		shminfo[i].shmid = shmget(IPC_PRIVATE,
    149 			(unsigned int)(ximage[i]->bytes_per_line * ximage[i]->height),
    150 			IPC_CREAT | 0777);
    151 
    152 		if(shminfo[i].shmid < 0) {
    153 			perror("shmget");
    154 			exit(-1);
    155 		}
    156 
    157 		shminfo[i].shmaddr = (char *)shmat(shminfo[i].shmid, 0, 0);
    158 
    159 		if (shminfo[i].shmaddr == ((char *) -1)) {
    160 			perror("shmat");
    161 			exit(-1);
    162 		}
    163 
    164 #ifdef DEBUG
    165 		fprintf(stderr, "new shared memory segment at 0x%08x size %d\n",
    166 			shminfo[i].shmaddr, ximage[i]->bytes_per_line * ximage[i]->height);
    167 #endif
    168 
    169 		ximage[i]->data = shminfo[i].shmaddr;
    170 		shminfo[i].readOnly = False;
    171 
    172 		XShmAttach(dpy, &shminfo[i]);
    173 		XSync(dpy, False);
    174 
    175 		shmctl(shminfo[i].shmid, IPC_RMID, 0);
    176 #else
    177 		char *data;
    178 		data = malloc(BitmapUnit(dpy) / 8 * width[i] * height[i]);
    179 
    180 		ximage[i] = XCreateImage(dpy,
    181 			DefaultVisualOfScreen(scr),
    182 			DefaultDepthOfScreen(scr),
    183 			ZPixmap, 0, data,
    184 			width[i], height[i], 32, 0);
    185 
    186 		if(ximage[i] == NULL) {
    187 			perror("XCreateImage");
    188 			exit(-1);
    189 		}
    190 
    191 #endif /* XSHM */
    192 	}
    193 	created_images = True;
    194 }
    195 
    196 void
    197 destroy_images(void) {
    198 	int i;
    199 
    200 	if (!created_images)
    201 		return;
    202 
    203 	for(i = 0; i < 2; i++) {
    204 #ifdef XSHM
    205 		XShmDetach(dpy, &shminfo[i]);	/* ask X11 to detach shared segment */
    206 		shmdt(shminfo[i].shmaddr);		/* detach it ourselves */
    207 #else
    208 		free(ximage[i]->data);
    209 #endif
    210 		ximage[i]->data = NULL;			/* remove refrence to that address */
    211 		XDestroyImage(ximage[i]);		/* and destroy image */
    212 	}
    213 
    214 	created_images = False;
    215 }
    216 
    217 void
    218 Usage(void) {
    219 	fprintf(stderr, "Usage: %s [ args ]\n"
    220 		"Command line args:\n"
    221 		"-display displayname\n"
    222 		"-mag magnification [ magnification ]\n"
    223 		"-geometry geometry\n"
    224 		"-source geometry\n"
    225 		"-dock\n"
    226 		"-x\n"
    227 		"-y\n"
    228 		"-xy\n\n"
    229 		"Window commands:\n"
    230 		"+: Zoom in\n"
    231 		"-: Zoom out\n"
    232 		"x: Flip right and left\n"
    233 		"y: Flip top and bottom\n"
    234 		"z: Rotate 90 degrees counter-clockwize\n"
    235 		"w: Next '+' or '-' only change width scaling\n"
    236 		"h: Next '+' or '-' only change height scaling\n"
    237 		"d: Change delay between frames\n"
    238 		"q: Quit\n"
    239 		"Arrow keys: Scroll in direction of arrow\n"
    240 		"Mouse button drag: Set top-left corner of viewed area\n",
    241 		progname);
    242 	exit(1);
    243 }
    244 
    245 /* resize is called with the dest size.
    246    we call it then manification changes or when
    247    actual window size is changed */
    248 void
    249 resize(int new_width, int new_height) {
    250 
    251 	destroy_images();		/* we can get rid of these */
    252 
    253 	/* find new dimensions for source */
    254 
    255 	if(flipxy) {
    256 		height[SRC] = (new_width+magx-1) / magx;
    257 		width[SRC] = (new_height+magy-1) / magy;
    258 	}
    259 	else {
    260 		width[SRC] = (new_width+magx-1) / magx;
    261 		height[SRC] = (new_height+magy-1) / magy;
    262 	}
    263 
    264 	if(width[SRC] < 1)
    265 		width[SRC] = 1;
    266 	if(width[SRC] > WidthOfScreen(scr))
    267 		width[SRC] = WidthOfScreen(scr);
    268 
    269 	if(height[SRC] < 1)
    270 		height[SRC] = 1;
    271 	if(height[SRC] > HeightOfScreen(scr))
    272 		height[SRC] = HeightOfScreen(scr);
    273 
    274 	/* temporary, the dest image may be larger than the
    275 	   actual window */
    276 	if(flipxy) {
    277 		width[DST] = magx * height[SRC];
    278 		height[DST] = magy * width[SRC];
    279 	}
    280 	else {
    281 		width[DST] = magx * width[SRC];
    282 		height[DST] = magy * height[SRC];
    283 	}
    284 
    285 	allocate_images();		/* allocate new images */
    286 
    287 	/* remember actual window size */
    288 	if(width[DST] > new_width)
    289 		width[DST] = new_width;
    290 	if(height[DST] > new_height)
    291 		height[DST] = new_height;
    292 }
    293 
    294 
    295 void scale8(void)
    296 {
    297 #define T unsigned char
    298 #include "scale.h"
    299 #undef T
    300 }
    301 
    302 
    303 void scale16(void)
    304 {
    305 #define T unsigned short
    306 #include "scale.h"
    307 #undef T
    308 }
    309 
    310 
    311 void scale32(void)
    312 {
    313 #define T unsigned int
    314 #include "scale.h"
    315 #undef T
    316 }
    317 
    318 
    319 void xzoom(int buttonpressed)
    320 {
    321 	char title[80];
    322 
    323 #ifdef XSHM
    324 	XShmGetImage(dpy, RootWindowOfScreen(scr), ximage[SRC],
    325 		     xgrab, ygrab, AllPlanes);
    326 #else
    327 	XGetSubImage(dpy, RootWindowOfScreen(scr),
    328 		     xgrab, ygrab, width[SRC], height[SRC], AllPlanes,
    329 		     ZPixmap, ximage[SRC], 0, 0);
    330 #endif
    331 #ifdef FRAME
    332 	if(buttonpressed) {	/* show the frame */
    333 		XDrawRectangle(dpy, RootWindowOfScreen(scr), framegc, xgrab, ygrab, width[SRC]-1, height[SRC]-1);
    334 		XSync(dpy, False);
    335 	}
    336 #endif
    337 
    338 	if (depth == 8)
    339 		scale8();
    340 	else if (depth <= 8*sizeof(short))
    341 		scale16();
    342 	else if (depth <= 8*sizeof(int))
    343 		scale32();
    344 
    345 #ifdef XSHM
    346 	XShmPutImage(dpy, win, gc, ximage[DST], 0, 0, 0, 0, width[DST], height[DST], False);
    347 #else
    348 	XPutImage(dpy, win, gc, ximage[DST], 0, 0, 0, 0, width[DST], height[DST]);
    349 #endif
    350 	if(set_title) {
    351 		if(magx == magy && !flipx && !flipy && !flipxy)
    352 			sprintf(title, "%s x%d", progname, magx);
    353 		else
    354 			sprintf(title, "%s X %s%d%s Y %s%d",
    355 				progname,
    356 				flipx?"-":"", magx,
    357 				flipxy?" <=>":";",
    358 				flipy?"-":"", magy);
    359 		XChangeProperty(dpy, win, XA_WM_NAME, XA_STRING, 8,
    360 				PropModeReplace,
    361 				(unsigned char *)title, strlen(title));
    362 		set_title = False;
    363 	}
    364 #ifdef TIMER
    365 	{
    366 		struct timeval current_time;
    367 		double DT;
    368 
    369 		gettimeofday(&current_time, NULL);
    370 		DT = current_time.tv_sec - old_time.tv_sec;
    371 		DT += 1e-6*(current_time.tv_usec - old_time.tv_usec);
    372 		sprintf(title, "DT=%6.3f", DT);
    373 		XDrawString(dpy, win, gc, 20, 20, title, strlen(title));
    374 		old_time = current_time;
    375 	}
    376 #endif
    377 	XSync(dpy, 0);
    378 }
    379 
    380 int
    381 main(int argc, char **argv) {
    382 	XSetWindowAttributes xswa;
    383 	XEvent event;
    384 	XTextProperty str;
    385 	XSizeHints *sizeh = NULL;
    386 	XClassHint *ch;
    387 	int buttonpressed = False;
    388 	int visible = False;
    389 	int scroll = 1;
    390 	char title[80];
    391 	XGCValues gcv;
    392 	char *dpyname = NULL;
    393 	int source_geom_mask = NoValue,
    394 	    dest_geom_mask = NoValue,
    395 	    copy_from_src_mask;
    396 	int xpos = 0, ypos = 0;
    397 	int isdock = 0;
    398 
    399 	atexit(destroy_images);
    400 	progname = strrchr(argv[0], '/');
    401 	if(progname)
    402 		++progname;
    403 	else
    404 		progname = argv[0];
    405 
    406 	/* parse command line options */
    407 	while(--argc > 0) {
    408 		++argv;
    409 
    410 		if(argv[0][0] == '=') {
    411 			dest_geom_mask = XParseGeometry(argv[0],
    412 				&xpos, &ypos,
    413 				&width[DST], &height[DST]);
    414 			continue;
    415 		}
    416 
    417 		if(!strcmp(argv[0], "-mag")) {
    418 			++argv; --argc;
    419 
    420 			magx = argc > 0 ? atoi(argv[0]) : -1;
    421 
    422 			if(magx <= 0)
    423 				Usage();
    424 
    425 
    426 			magy = argc > 1 ? atoi(argv[1]) : -1;
    427 
    428 			if(magy <= 0)
    429 				magy = magx;
    430 			else {
    431 				++argv; --argc;
    432 			}
    433 
    434 			continue;
    435 		}
    436 
    437 		if(!strcmp(argv[0], "-x")) {
    438 			flipx = True;
    439 			continue;
    440 		}
    441 
    442 		if(!strcmp(argv[0], "-y")) {
    443 			flipy = True;
    444 			continue;
    445 		}
    446 
    447 		if(!strcmp(argv[0], "-z") ||
    448 		   !strcmp(argv[0], "-xy")) {
    449 			flipxy = True;
    450 			continue;
    451 		}
    452 
    453 		if(!strcmp(argv[0], "-source")) {
    454 			++argv; --argc;
    455 
    456 			if(argc < 1)
    457 				Usage();
    458 
    459 			source_geom_mask = XParseGeometry(argv[0],
    460 				&xgrab, &ygrab,
    461 				&width[SRC], &height[SRC]);
    462 
    463 			continue;
    464 		}
    465 
    466 		if(!strcmp(argv[0], "-dest") ||
    467 		   !strcmp(argv[0], "-geometry")) {
    468 			++argv; --argc;
    469 
    470 			if(argc < 1)
    471 				Usage();
    472 
    473 			dest_geom_mask = XParseGeometry(argv[0],
    474 				&xpos, &ypos,
    475 				&width[DST], &height[DST]);
    476 
    477 			continue;
    478 		}
    479 
    480 		if(!strcmp(argv[0], "-d") ||
    481 		   !strcmp(argv[0], "-display")) {
    482 
    483 			++argv; --argc;
    484 
    485 			if(argc < 1)
    486 				Usage();
    487 
    488 			dpyname = argv[0];
    489 			continue;
    490 		}
    491 
    492 		if(!strcmp(argv[0], "-delay")) {
    493 
    494 			++argv; --argc;
    495 
    496 			if(argc < 1)
    497 				Usage();
    498 
    499 			if(sscanf(argv[0], "%u", &delay) != 1)
    500 				Usage();
    501 
    502 			delay *= 1000;
    503 
    504 			continue;
    505 		}
    506 
    507 		if(!strcmp(argv[0], "-dock")) {
    508 
    509 			isdock = 1;
    510 
    511 			continue;
    512 		}
    513 
    514 		Usage();
    515 	}
    516 
    517 	if (!(dpy = XOpenDisplay(dpyname))) {
    518 		perror("Cannot open display");
    519 		exit(-1);
    520 	}
    521 
    522 	/* Now, see if we have to calculate width[DST] and height[DST]
    523 	   from the SRC parameters */
    524 
    525 	copy_from_src_mask = NoValue;
    526 
    527 	if(source_geom_mask & WidthValue) {
    528 		if(flipxy) {
    529 			height[DST] = magy * width[SRC];
    530 			copy_from_src_mask |= HeightValue;
    531 
    532 		}
    533 		else {
    534 			width[DST] = magx * width[SRC];
    535 			copy_from_src_mask |= WidthValue;
    536 		}
    537 	}
    538 
    539 	if(source_geom_mask & HeightValue) {
    540 		if(flipxy) {
    541 			width[DST] = magx * height[SRC];
    542 			copy_from_src_mask |= WidthValue;
    543 		}
    544 		else {
    545 			height[DST] = magy * height[SRC];
    546 			copy_from_src_mask |= HeightValue;
    547 		}
    548 	}
    549 
    550 	if(copy_from_src_mask & dest_geom_mask) {
    551 		fprintf(stderr, "Conflicting dimensions between source and dest geometry\n");
    552 		Usage();
    553 	}
    554 
    555 	scr = DefaultScreenOfDisplay(dpy);
    556 
    557 	depth = DefaultDepthOfScreen(scr);
    558 	if (depth < 8) {
    559 		fprintf(stderr, "%s: need at least 8 bits/pixel\n", progname);
    560 		exit(1);
    561 	}
    562 
    563 	if(source_geom_mask & XNegative)
    564 		xgrab += WidthOfScreen(scr);
    565 
    566 	if(source_geom_mask & YNegative)
    567 		ygrab += HeightOfScreen(scr);
    568 
    569 	if(dest_geom_mask & XNegative)
    570 		xpos += WidthOfScreen(scr);
    571 
    572 	if(dest_geom_mask & YNegative)
    573 		ypos += HeightOfScreen(scr);
    574 
    575 	printf("=%dx%d+%d+%d\n", width[DST], height[DST], xpos, ypos);
    576 
    577 	xswa.event_mask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask;
    578 	xswa.event_mask |= StructureNotifyMask;	/* resize etc.. */
    579 	xswa.event_mask |= KeyPressMask|KeyReleaseMask;		/* commands */
    580 	xswa.event_mask |= VisibilityChangeMask;
    581 
    582 	xswa.background_pixel = BlackPixelOfScreen(scr);
    583 
    584 	win = XCreateWindow(dpy, RootWindowOfScreen(scr),
    585 	    xpos, ypos, width[DST], height[DST], 0,
    586 	    DefaultDepthOfScreen(scr), InputOutput,
    587 	    DefaultVisualOfScreen(scr),
    588 	    CWEventMask | CWBackPixel, &xswa);
    589 
    590 	XChangeProperty(dpy, win, XA_WM_ICON_NAME, XA_STRING, 8,
    591 			PropModeReplace,
    592 			(unsigned char *)progname, strlen(progname));
    593 
    594 	if(!isdock) {
    595 		sizeh = XAllocSizeHints();
    596 		sizeh->flags = PMaxSize | PMinSize;
    597 		sizeh->min_width = sizeh->max_width = width[DST];
    598 		sizeh->min_height = sizeh->max_height = height[DST];
    599 	}
    600 	XStringListToTextProperty(&progname, 1, &str);
    601 	ch = XAllocClassHint();
    602 	ch->res_class = progname;
    603 	ch->res_name = progname;
    604 
    605 	XSetWMProperties(dpy, win, &str, &str, NULL, 0, sizeh, NULL, ch);
    606 
    607 	if(isdock) {
    608 		wm_windowtype = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
    609 		wm_dock = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
    610 
    611 		XChangeProperty(dpy, win, wm_windowtype, XA_ATOM, 32,
    612 				PropModeReplace, (unsigned char *)&wm_dock,
    613 				1);
    614 	}
    615 
    616 	/*
    617 	XChangeProperty(dpy, win, XA_WM_NAME, XA_STRING, 8,
    618 			PropModeReplace,
    619 			(unsigned char *)progname, strlen(progname));
    620 	*/
    621 
    622 
    623 	/***	20020213
    624 		code added by <tmancill@debian.org> to handle
    625 		window manager "close" event
    626 	***/
    627 	wm_delete_window = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
    628 	wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
    629         status = XSetWMProtocols(dpy, win, &wm_delete_window, 1);
    630 
    631 	set_title = True;
    632 
    633 	status = XMapWindow(dpy, win);
    634 
    635 	gcv.plane_mask = AllPlanes;
    636 	gcv.subwindow_mode = IncludeInferiors;
    637 	gcv.function = GXcopy;
    638 	gcv.foreground = WhitePixelOfScreen(scr);
    639 	gcv.background = BlackPixelOfScreen(scr);
    640 	gc = XCreateGC(dpy, RootWindowOfScreen(scr),
    641 		GCFunction|GCPlaneMask|GCSubwindowMode|GCForeground|GCBackground,
    642 		&gcv);
    643 
    644 #ifdef FRAME
    645 	gcv.foreground = AllPlanes;
    646 	gcv.plane_mask = WhitePixelOfScreen(scr)^BlackPixelOfScreen(scr);
    647 	gcv.subwindow_mode = IncludeInferiors;
    648 	gcv.function = GXxor;
    649 	framegc = XCreateGC(dpy, RootWindowOfScreen(scr),
    650 		GCFunction|GCPlaneMask|GCSubwindowMode|GCForeground,
    651 		&gcv);
    652 #endif
    653 
    654 #ifdef TIMER
    655 	font = XLoadFont(dpy, "fixed");
    656 #endif
    657 
    658 	resize(width[DST], height[DST]);
    659 
    660 #ifdef FRAME
    661 
    662 	{
    663 		static char bitmap_data[] = { 0 };
    664 		static XColor col = { 0 };
    665 		Pixmap curs = XCreatePixmapFromBitmapData(dpy,
    666 			RootWindowOfScreen(scr), bitmap_data, 1, 1, 0, 0, 1);
    667 
    668 		when_button = XCreatePixmapCursor(dpy, curs, curs, &col, &col, 0, 0);
    669 	}
    670 #else
    671 	when_button = XCreateFontCursor(dpy, XC_ul_angle);
    672 #endif
    673 	crosshair = XCreateFontCursor(dpy, XC_crosshair);
    674 
    675 	XDefineCursor(dpy, win, crosshair);
    676 
    677 	for(;;) {
    678 
    679 		/*****
    680 		old event loop updated to support WM messages
    681 		while(unmapped?
    682 			(XWindowEvent(dpy, win, (long)-1, &event), 1):
    683 			XCheckWindowEvent(dpy, win, (long)-1, &event)) {
    684 		******/
    685 
    686 		while(XPending(dpy)) {
    687 			XNextEvent(dpy, &event);
    688 			switch(event.type) {
    689 			case ClientMessage:
    690 				if ((event.xclient.message_type == wm_protocols) &&
    691                                     (event.xclient.data.l[0] == wm_delete_window)) {
    692 					exit(0);
    693 				}
    694 				break;
    695 			case ConfigureNotify:
    696 				if(event.xconfigure.width != width[DST] ||
    697 				   event.xconfigure.height != height[DST]) {
    698 
    699 					resize(event.xconfigure.width, event.xconfigure.height);
    700 				}
    701 				break;
    702 			case ReparentNotify:
    703 				break;	/* what do we do with it? */
    704 
    705 			case VisibilityNotify:
    706 				/* VisibilityUnobscured, VisibilityPartiallyObscured, or VisibilityFullyObscured */
    707 				visible = (event.xvisibility.state!=VisibilityFullyObscured);
    708 				break;
    709 
    710 			case KeyRelease:
    711 				switch(XKeycodeToKeysym(dpy, event.xkey.keycode, 0)) {
    712 				case XK_Control_L:
    713 				case XK_Control_R:
    714 					scroll = 1;
    715 					break;
    716 				}
    717 				break;
    718 
    719 			case KeyPress:
    720 				switch(XKeycodeToKeysym(dpy, event.xkey.keycode, 0)) {
    721 				case XK_Control_L:
    722 				case XK_Control_R:
    723 					scroll = 10;
    724 					break;
    725 
    726 				case '+':
    727 				case '=':
    728 				case XK_KP_Add:
    729 					if(!yzoom_flag) ++magx;
    730 					if(!xzoom_flag) ++magy;
    731 					xzoom_flag = yzoom_flag = False;
    732 					resize(width[DST], height[DST]);
    733 					set_title = True;
    734 					break;
    735 
    736 				case '-':
    737 				case XK_KP_Subtract:
    738 					if(!yzoom_flag) --magx;
    739 					if(!xzoom_flag) --magy;
    740 					xzoom_flag = yzoom_flag = False;
    741 					if(magx < 1) magx = 1;
    742 					if(magy < 1) magy = 1;
    743 					resize(width[DST], height[DST]);
    744 					set_title = True;
    745 					break;
    746 
    747 				case XK_Left:
    748 				case XK_KP_Left:
    749 					if(flipxy)
    750 						if(flipx)
    751 							ygrab += scroll;
    752 						else
    753 							ygrab -= scroll;
    754 					else
    755 						if(flipx)
    756 							xgrab += scroll;
    757 						else
    758 							xgrab -= scroll;
    759 					break;
    760 
    761 				case XK_Right:
    762 				case XK_KP_Right:
    763 					if(flipxy)
    764 						if(flipx)
    765 							ygrab -= scroll;
    766 						else
    767 							ygrab += scroll;
    768 					else
    769 						if(flipx)
    770 							xgrab -= scroll;
    771 						else
    772 							xgrab += scroll;
    773 					break;
    774 
    775 				case XK_Up:
    776 				case XK_KP_Up:
    777 					if(flipxy)
    778 						if(flipy)
    779 							xgrab -= scroll;
    780 						else
    781 							xgrab += scroll;
    782 					else
    783 						if(flipy)
    784 							ygrab += scroll;
    785 						else
    786 							ygrab -= scroll;
    787 					break;
    788 
    789 				case XK_Down:
    790 				case XK_KP_Down:
    791 					if(flipxy)
    792 						if(flipy)
    793 							xgrab += scroll;
    794 						else
    795 							xgrab -= scroll;
    796 					else
    797 						if(flipy)
    798 							ygrab -= scroll;
    799 						else
    800 							ygrab += scroll;
    801 					break;
    802 
    803 				case 'x':
    804 					flipx = !flipx;
    805 					set_title = True;
    806 					break;
    807 
    808 				case 'y':
    809 					flipy = !flipy;
    810 					set_title = True;
    811 					break;
    812 
    813 				case 'z':
    814 					if(flipx^flipy^flipxy) {
    815 						flipx = !flipx;
    816 						flipy = !flipy;
    817 					}
    818 					flipxy = !flipxy;
    819 					resize(width[DST], height[DST]);
    820 					set_title = True;
    821 					break;
    822 
    823 				case 'w':
    824 					xzoom_flag = True;
    825 					yzoom_flag = False;
    826 					break;
    827 
    828 				case 'h':
    829 					yzoom_flag = True;
    830 					xzoom_flag = False;
    831 					break;
    832 
    833 				case 'g':
    834 					gridx = !gridx;
    835 					gridy = !gridy;
    836 					break;
    837 
    838 				case 'd':
    839 					if(++delay_index >= NDELAYS)
    840 						delay_index = 0;
    841 					delay = delays[delay_index];
    842 					sprintf(title, "delay = %d ms", delay/1000);
    843 					XChangeProperty(dpy, win, XA_WM_NAME, XA_STRING, 8,
    844 						PropModeReplace,
    845 						(unsigned char *)title, strlen(title));
    846 					signal(SIGALRM, timeout_func);
    847 					alarm(2);
    848 					break;
    849 
    850 				case 'q':
    851 					exit(0);
    852 					break;
    853 				}
    854 
    855 				break;
    856 
    857 			case ButtonPress:
    858 #ifdef FRAME
    859 				xgrab = event.xbutton.x_root - width[SRC]/2;
    860 				ygrab = event.xbutton.y_root - height[SRC]/2;
    861 #else
    862 				xgrab = event.xbutton.x_root;
    863 				ygrab = event.xbutton.y_root;
    864 #endif
    865 				XDefineCursor(dpy, win, when_button);
    866 				buttonpressed = True;
    867 				break;
    868 
    869 			case ButtonRelease:
    870 				/*
    871 				xgrab = event.xbutton.x_root - width[SRC]/2;
    872 				ygrab = event.xbutton.y_root - height[SRC]/2;
    873 				*/
    874 				XDefineCursor(dpy, win, crosshair);
    875 				buttonpressed = False;
    876 				break;
    877 
    878 			case MotionNotify:
    879 				if(buttonpressed) {
    880 #ifdef FRAME
    881 					xgrab = event.xmotion.x_root - width[SRC]/2;
    882 					ygrab = event.xmotion.y_root - height[SRC]/2;
    883 #else
    884 					xgrab = event.xmotion.x_root;
    885 					ygrab = event.xmotion.y_root;
    886 #endif
    887 				}
    888 				break;
    889 
    890 			}
    891 
    892 			/* trying XShmGetImage when part of the rect is
    893 			   not on the screen will fail LOUDLY..
    894 			   we have to veryfy this after anything that may
    895 			   may modified xgrab or ygrab or the size of
    896 			   the source ximage */
    897 
    898 			if(xgrab < 0)
    899 				xgrab = 0;
    900 
    901 			if(xgrab > WidthOfScreen(scr)-width[SRC])
    902 				xgrab =  WidthOfScreen(scr)-width[SRC];
    903 
    904 			if(ygrab < 0)
    905 				ygrab = 0;
    906 
    907 			if(ygrab > HeightOfScreen(scr)-height[SRC])
    908 				ygrab = HeightOfScreen(scr)-height[SRC];
    909 
    910 		}
    911 
    912 		if(visible)
    913 			xzoom(buttonpressed);
    914 
    915 #ifdef NO_USLEEP
    916 #define usleep(_t)								\
    917 	{											\
    918 		struct timeval timeout;					\
    919 		timeout.tv_sec =  0;					\
    920 		timeout.tv_usec = _t;					\
    921 		select(0, NULL, NULL, NULL, &timeout);	\
    922 	}
    923 #endif
    924 
    925 		if(!buttonpressed && delay > 0)
    926 			usleep(delay);
    927 #ifdef FRAME
    928 		if(buttonpressed) {
    929 			XDrawRectangle(dpy, RootWindowOfScreen(scr),
    930 					framegc, xgrab, ygrab, width[SRC]-1,
    931 					height[SRC]-1);
    932 		}
    933 #endif
    934 	}
    935 }