1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stddef.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <math.h>
8 #include <signal.h>
9 
10 #include <X11/X.h>
11 #include <X11/Xlib.h>
12 #include <X11/Intrinsic.h>
13 #include <X11/StringDefs.h>
14 #include <X11/cursorfont.h>
15 #include <X11/extensions/XShm.h>
16 
17 #include "ida.h"
18 #include "x11.h"
19 #include "dither.h"
20 #include "readers.h"
21 #include "viewer.h"
22 #include "hex.h"
23 #include "idaconfig.h"
24 
25 /* ----------------------------------------------------------------------- */
26 
27 #define POINTER_NORMAL    0
28 #define POINTER_BUSY      1
29 #define POINTER_PICK      2
30 #define RUBBER_NEW        3
31 #define RUBBER_MOVE       4
32 #define RUBBER_X1         5
33 #define RUBBER_Y1         6
34 #define RUBBER_X2         7
35 #define RUBBER_Y2         8
36 
37 #define RUBBER_RANGE      6
38 #define RUBBER_INTERVAL 100
39 
40 #define PROCESS_LINES    16
41 
42 extern int debug;
43 Cursor ptrs[POINTER_COUNT];
44 
45 /* ----------------------------------------------------------------------- */
46 
image_to_pixmap(struct ida_image * img)47 Pixmap image_to_pixmap(struct ida_image *img)
48 {
49     unsigned char line[256],*src;
50     XImage *ximage;
51     void *shm;
52     Pixmap pix;
53     GC gc;
54     unsigned int x,y;
55 
56     ximage = x11_create_ximage(app_shell, img->i.width, img->i.height, &shm);
57     for (y = 0; y < img->i.height; y++) {
58 	src = ida_image_scanline(img, y);
59 	if (display_type == PSEUDOCOLOR) {
60 	    dither_line(src, line, y, img->i.width);
61 	    for (x = 0; x < img->i.width; x++)
62 		XPutPixel(ximage, x, y, x11_map[line[x]]);
63 	} else {
64 	    for (x = 0; x < img->i.width; x++, src += 3) {
65 		pix = x11_lut_red[src[0]] |
66 		    x11_lut_green[src[1]] |
67 		    x11_lut_blue[src[2]];
68 		XPutPixel(ximage, x, y, pix);
69 	    }
70 	}
71     }
72     pix = XCreatePixmap(dpy,XtWindow(app_shell),img->i.width, img->i.height,
73 			DefaultDepthOfScreen(XtScreen(app_shell)));
74     gc = XCreateGC(dpy, pix, 0, NULL);
75     XPUTIMAGE(dpy, pix, gc, ximage, 0, 0, 0, 0, img->i.width, img->i.height);
76     XFreeGC(dpy, gc);
77     x11_destroy_ximage(app_shell, ximage, shm);
78     return pix;
79 }
80 
81 /* ----------------------------------------------------------------------- */
82 
viewer_i2s(int zoom,int val)83 int viewer_i2s(int zoom, int val)
84 {
85     if (0 > zoom)
86 	return val/(-zoom+1);
87     if (0 < zoom)
88 	return val*(zoom+1);
89     return val;
90 }
91 
92 /* ----------------------------------------------------------------------- */
93 
94 static void
viewer_renderline(struct ida_viewer * ida,char * scanline)95 viewer_renderline(struct ida_viewer *ida, char *scanline)
96 {
97     unsigned char *src,*dst,*rgb;
98     unsigned long pix;
99     unsigned int x,s,scrline;
100 
101     src = scanline;
102 
103     if (0 == ida->zoom) {
104 	/* as-is */
105 	if (display_type == PSEUDOCOLOR) {
106 	    dst = ida->dither_line;
107 	    dither_line(src, dst, ida->line, ida->scrwidth);
108 	    for (x = 0; x < ida->scrwidth; x++, dst++)
109 		XPutPixel(ida->ximage, x, ida->line, x11_map[*dst]);
110 	} else {
111 	    for (x = 0; x < ida->scrwidth; x++, src += 3) {
112 		pix = x11_lut_red[src[0]] |
113 		    x11_lut_green[src[1]] |
114 		    x11_lut_blue[src[2]];
115 		XPutPixel(ida->ximage, x, ida->line, pix);
116 	    }
117 	}
118 
119     } else if (ida->zoom < 0) {
120 	/* zoom out */
121 	s = -ida->zoom+1;
122 	if (s-1 != (ida->line % s))
123 	    return;
124 	scrline = ida->line/s;
125 	if (display_type == PSEUDOCOLOR) {
126 	    rgb = ida->rgb_line;
127 	    for (x = 0; x < ida->scrwidth; x++, rgb += 3, src += 3*s) {
128 		rgb[0] = src[0];
129 		rgb[1] = src[1];
130 		rgb[2] = src[2];
131 	    }
132 	    rgb = ida->rgb_line;
133 	    dst = ida->dither_line;
134 	    dither_line(rgb, dst, scrline, ida->scrwidth);
135 	    for (x = 0; x < ida->scrwidth; x++, dst++)
136 		XPutPixel(ida->ximage, x, scrline, x11_map[*dst]);
137 	} else {
138 #if 0
139 	    /* just drop pixels */
140 	    for (x = 0; x < ida->scrwidth; x++, src += 3*s) {
141 		pix = x11_lut_red[src[0]] |
142 		    x11_lut_green[src[1]] |
143 		    x11_lut_blue[src[2]];
144 		XPutPixel(ida->ximage, x, scrline, pix);
145 	    }
146 #else
147 	    /* horizontal interpolation (vertical is much harder ...) */
148 	    for (x = 0; x < ida->scrwidth; x++, src += 3*s) {
149 		int red,green,blue,count,ix;
150 		red   = 0;
151 		green = 0;
152 		blue  = 0;
153 		count = 0;
154 		for (ix = 0; ix < 3*s; ix += 3) {
155 		    red   += src[ix+0];
156 		    green += src[ix+1];
157 		    blue  += src[ix+2];
158 		    count += 1;
159 		}
160 		pix = x11_lut_red[red/count] |
161 		    x11_lut_green[green/count] |
162 		    x11_lut_blue[blue/count];
163 		XPutPixel(ida->ximage, x, scrline, pix);
164 	    }
165 #endif
166 	}
167 
168     } else {
169 	/* zoom in */
170 	s = ida->zoom+1;
171 	if (display_type == PSEUDOCOLOR) {
172 	    rgb = ida->rgb_line;
173 	    for (x = 0; x < ida->scrwidth; rgb += 3) {
174 		rgb[0] = src[0];
175 		rgb[1] = src[1];
176 		rgb[2] = src[2];
177 		x++;
178 		if (0 == (x%s))
179 		    src += 3;
180 	    }
181 	    for (scrline = ida->line*s; scrline < ida->line*s+s; scrline++) {
182 		rgb = ida->rgb_line;
183 		dst = ida->dither_line;
184 		dither_line(rgb, dst, scrline, ida->scrwidth);
185 		for (x = 0; x < ida->scrwidth; x++, dst++)
186 		    XPutPixel(ida->ximage, x, scrline, x11_map[*dst]);
187 	    }
188 	} else {
189 	    for (scrline = ida->line*s; scrline < ida->line*s+s; scrline++) {
190 		src = scanline;
191 		for (x = 0; x < ida->scrwidth; src += 3) {
192 		    unsigned int i;
193 		    pix = x11_lut_red[src[0]] |
194 			x11_lut_green[src[1]] |
195 			x11_lut_blue[src[2]];
196 		    for (i = 0; i < s; i++, x++)
197 			XPutPixel(ida->ximage, x, scrline, pix);
198 		}
199 	    }
200 	}
201     }
202 }
203 
204 /* ----------------------------------------------------------------------- */
205 
206 static void
viewer_cleanup(struct ida_viewer * ida)207 viewer_cleanup(struct ida_viewer *ida)
208 {
209     if (ida->load_read) {
210 	ida->load_done(ida->load_data);
211 	ida->load_line = 0;
212 	ida->load_read = NULL;
213 	ida->load_done = NULL;
214 	ida->load_data = NULL;
215     }
216     if (ida->op_work) {
217 	ida->op_done(ida->op_data);
218 	ida->op_line = 0;
219 	ida->op_work = NULL;
220 	ida->op_done = NULL;
221 	ida->op_data = NULL;
222 	if (ida->op_src.p) {
223 	    if (ida->undo.p) {
224 		fprintf(stderr,"have undo buffer /* shouldn't happen */");
225 		ida_image_free(&ida->undo);
226 	    }
227 	    ida->undo = ida->op_src;
228 	    memset(&ida->op_src,0,sizeof(ida->op_src));
229 	}
230     }
231 }
232 
233 static Boolean
viewer_workproc(XtPointer client_data)234 viewer_workproc(XtPointer client_data)
235 {
236     struct ida_viewer *ida = client_data;
237     unsigned int start,end;
238     char *scanline;
239 
240     start = ida->line;
241     end   = ida->line + ida->steps;
242     if (end > ida->img.i.height)
243 	end = ida->img.i.height;
244 
245     /* image loading */
246     if (ida->load_read) {
247 	for (ida->line = start; ida->line < end; ida->line++) {
248 	    if (ida->load_line > ida->line)
249 		continue;
250 	    scanline = ida_image_scanline(&ida->img, ida->load_line);
251 	    ida->load_read(scanline,ida->load_line,ida->load_data);
252 	    ida->load_line++;
253 	}
254     }
255 
256     /* image processing */
257     if (ida->op_work  &&  0 == ida->op_preview) {
258 	for (ida->line = start; ida->line < end; ida->line++) {
259 	    if (ida->op_line > ida->line)
260 		continue;
261 	    scanline = ida_image_scanline(&ida->img, ida->op_line);
262 	    ida->op_work(&ida->op_src,&ida->op_rect,
263 			scanline,ida->op_line,ida->op_data);
264 	    ida->op_line++;
265 	}
266     }
267 
268     /* image rendering */
269     if (ida->op_work  && ida->op_preview) {
270 	for (ida->line = start; ida->line < end; ida->line++) {
271 	    ida->op_line = ida->line;
272 	    ida->op_work(&ida->img,&ida->op_rect,
273 			 ida->preview_line,ida->line,ida->op_data);
274 	    viewer_renderline(ida,ida->preview_line);
275 	}
276     } else {
277 	for (ida->line = start; ida->line < end; ida->line++) {
278 	    scanline = ida_image_scanline(&ida->img, ida->line);
279 	    viewer_renderline(ida,scanline);
280 	}
281     }
282 
283     /* trigger redraw */
284     XClearArea(XtDisplay(ida->widget), XtWindow(ida->widget),
285 	       0, viewer_i2s(ida->zoom,start),
286 	       ida->scrwidth, viewer_i2s(ida->zoom,ida->steps), True);
287 
288     /* all done ? */
289     if (ida->line == ida->img.i.height) {
290 	viewer_cleanup(ida);
291 	ida->wproc = 0;
292 #if 1
293 	if (args.testload)
294 	    XtCallActionProc(ida->widget,"Next",NULL,NULL,0);
295 #endif
296 	return TRUE;
297     }
298     return FALSE;
299 }
300 
viewer_workstart(struct ida_viewer * ida)301 static void viewer_workstart(struct ida_viewer *ida)
302 {
303     /* (re-) start */
304     ida->line  = 0;
305     if (!ida->wproc)
306 	ida->wproc = XtAppAddWorkProc(app_context,viewer_workproc,ida);
307 }
308 
viewer_workstop(struct ida_viewer * ida)309 static void viewer_workstop(struct ida_viewer *ida)
310 {
311     if (!ida->wproc)
312 	return;
313 
314     viewer_cleanup(ida);
315     XtRemoveWorkProc(ida->wproc);
316     ida->wproc = 0;
317 }
318 
viewer_workfinish(struct ida_viewer * ida)319 static void viewer_workfinish(struct ida_viewer *ida)
320 {
321     char *scanline;
322 
323     if (ida->load_read) {
324 	for (ida->line = ida->load_line; ida->line < ida->img.i.height;) {
325 	    scanline = ida_image_scanline(&ida->img, ida->line);
326 	    ida->load_read(scanline,ida->load_line,ida->load_data);
327 	    ida->line++;
328 	    ida->load_line++;
329 	}
330     }
331     if (ida->op_work && 0 == ida->op_preview) {
332 	for (ida->line = ida->op_line; ida->line < ida->img.i.height;) {
333 	    scanline = ida_image_scanline(&ida->img, ida->line);
334 	    ida->op_work(&ida->op_src,&ida->op_rect,
335 			scanline,ida->op_line,ida->op_data);
336 	    ida->line++;
337 	    ida->op_line++;
338 	}
339     }
340     viewer_workstop(ida);
341 }
342 
343 /* ----------------------------------------------------------------------- */
344 
345 static void
viewer_new_view(struct ida_viewer * ida)346 viewer_new_view(struct ida_viewer *ida)
347 {
348     if (NULL != ida->ximage)
349 	x11_destroy_ximage(ida->widget,ida->ximage,ida->ximage_shm);
350     if (NULL != ida->rgb_line)
351 	free(ida->rgb_line);
352     if (NULL != ida->dither_line)
353 	free(ida->dither_line);
354     if (NULL != ida->preview_line)
355 	free(ida->preview_line);
356 
357     ida->scrwidth  = viewer_i2s(ida->zoom,ida->img.i.width);
358     ida->scrheight = viewer_i2s(ida->zoom,ida->img.i.height);
359     ida->steps = PROCESS_LINES;
360     if (ida->zoom < 0)
361 	while ((ida->steps % (-ida->zoom+1)) != 0)
362 	    ida->steps++;
363 
364     ida->rgb_line = malloc(ida->scrwidth*3);
365     ida->dither_line = malloc(ida->scrwidth);
366     ida->preview_line = malloc(ida->img.i.width*3);
367     ida->ximage = x11_create_ximage(ida->widget, ida->scrwidth, ida->scrheight,
368 				   &ida->ximage_shm);
369     if (NULL == ida->ximage) {
370 	ida->zoom--;
371 	return viewer_new_view(ida);
372     }
373     XtVaSetValues(ida->widget,
374 		  XtNwidth,  ida->scrwidth,
375 		  XtNheight, ida->scrheight,
376 		  NULL);
377     viewer_workstart(ida);
378 }
379 
380 static void
381 viewer_timeout(XtPointer client_data, XtIntervalId *id);
382 
383 static int
viewer_rubber_draw(struct ida_viewer * ida)384 viewer_rubber_draw(struct ida_viewer *ida)
385 {
386     XGCValues values;
387     struct ida_rect r = ida->current;
388     int x,y,w,h;
389 
390     values.function   = GXxor;
391     values.foreground = ida->mask;
392     XChangeGC(dpy,ida->wgc,GCFunction|GCForeground,&values);
393     if (r.x1 < r.x2) {
394 	x = viewer_i2s(ida->zoom,r.x1);
395 	w = viewer_i2s(ida->zoom,r.x2 - r.x1);
396     } else {
397 	x = viewer_i2s(ida->zoom,r.x2);
398 	w = viewer_i2s(ida->zoom,r.x1 - r.x2);
399     }
400     if (r.y1 < r.y2) {
401 	y = viewer_i2s(ida->zoom,r.y1);
402 	h = viewer_i2s(ida->zoom,r.y2 - r.y1);
403     } else {
404 	y = viewer_i2s(ida->zoom,r.y2);
405 	h = viewer_i2s(ida->zoom,r.y1 - r.y2);
406     }
407     if (0 == h && 0 == w)
408 	return 0;
409     if (w)
410 	w--;
411     if (h)
412 	h--;
413     XDrawRectangle(dpy,XtWindow(ida->widget),ida->wgc,x,y,w,h);
414     return 1;
415 }
416 
417 static void
viewer_rubber_off(struct ida_viewer * ida)418 viewer_rubber_off(struct ida_viewer *ida)
419 {
420     if (ida->marked)
421 	viewer_rubber_draw(ida);
422     ida->marked = 0;
423     if (ida->timer)
424 	XtRemoveTimeOut(ida->timer);
425     ida->timer = 0;
426 }
427 
428 static void
viewer_rubber_on(struct ida_viewer * ida)429 viewer_rubber_on(struct ida_viewer *ida)
430 {
431     ida->marked = viewer_rubber_draw(ida);
432     if (ida->marked)
433 	ida->timer = XtAppAddTimeOut(app_context,RUBBER_INTERVAL,
434 				    viewer_timeout,ida);
435 }
436 
437 static void
viewer_timeout(XtPointer client_data,XtIntervalId * id)438 viewer_timeout(XtPointer client_data, XtIntervalId *id)
439 {
440     struct ida_viewer *ida = client_data;
441 
442     ida->timer = 0;
443     viewer_rubber_off(ida);
444     ida->mask <<= 1;
445     if ((ida->mask & 0x10) == 0x10)
446 	ida->mask |= 0x01;
447     viewer_rubber_on(ida);
448 }
449 
450 static void
viewer_redraw(Widget widget,XtPointer client_data,XEvent * ev,Boolean * cont)451 viewer_redraw(Widget widget, XtPointer client_data,
452 	      XEvent *ev, Boolean *cont)
453 {
454     struct ida_viewer *ida = client_data;
455     XExposeEvent *event;
456     XGCValues values;
457 
458     if (ev->type != Expose)
459 	return;
460     event = (XExposeEvent*)ev;
461 
462     if (NULL == ida->ximage)
463 	return;
464     if (event->x + event->width > (int)ida->scrwidth)
465 	return;
466     if (event->y + event->height > (int)ida->scrheight)
467 	return;
468     if (NULL == ida->wgc)
469 	ida->wgc = XCreateGC(XtDisplay(widget), XtWindow(widget), 0, NULL);
470 
471     viewer_rubber_off(ida);
472     values.function   = GXcopy;
473     XChangeGC(dpy,ida->wgc,GCFunction,&values);
474     XPUTIMAGE(XtDisplay(ida->widget), XtWindow(widget),
475 	      ida->wgc, ida->ximage,
476 	      event->x, event->y, event->x, event->y,
477 	      event->width, event->height);
478     viewer_rubber_on(ida);
479 }
480 
481 static int
viewer_pos2state(struct ida_viewer * ida,int x,int y)482 viewer_pos2state(struct ida_viewer *ida, int x, int y)
483 {
484     int x1,x2,y1,y2;
485 
486     if (POINTER_PICK == ida->state)
487 	return ida->state;
488 
489     x1 = viewer_i2s(ida->zoom,ida->current.x1);
490     x2 = viewer_i2s(ida->zoom,ida->current.x2);
491     y1 = viewer_i2s(ida->zoom,ida->current.y1);
492     y2 = viewer_i2s(ida->zoom,ida->current.y2);
493     if ((x1 < x && x < x2) || (x2 < x && x < x1)) {
494 	if (y1-RUBBER_RANGE < y && y < y1+RUBBER_RANGE)
495 	    return RUBBER_Y1;
496 	if (y2-RUBBER_RANGE < y && y < y2+RUBBER_RANGE)
497 	    return RUBBER_Y2;
498     }
499     if ((y1 < y && y < y2) || (y2 < y && y < y1)) {
500 	if (x1-RUBBER_RANGE < x && x < x1+RUBBER_RANGE)
501 	    return RUBBER_X1;
502 	if (x2-RUBBER_RANGE < x && x < x2+RUBBER_RANGE)
503 	    return RUBBER_X2;
504     }
505     if (((x1 < x && x < x2) || (x2 < x && x < x1)) &&
506 	((y1 < y && y < y2) || (y2 < y && y < y1)))
507 	return RUBBER_MOVE;
508     return RUBBER_NEW;
509 }
510 
511 static void
viewer_mouse(Widget widget,XtPointer client_data,XEvent * ev,Boolean * cont)512 viewer_mouse(Widget widget, XtPointer client_data,
513 	     XEvent *ev, Boolean *cont)
514 {
515     struct ida_viewer *ida = client_data;
516     int state = POINTER_NORMAL;
517     unsigned char *pix;
518     int x,y;
519 
520     viewer_rubber_off(ida);
521 
522     switch (ev->type) {
523     case ButtonPress:
524     {
525 	XButtonEvent *eb = (XButtonEvent*)ev;
526 
527 	if (eb->button != Button1)
528 	    goto out;
529 	ida->state = viewer_pos2state(ida,eb->x,eb->y);
530 	switch (ida->state) {
531 	case POINTER_PICK:
532 	    x = viewer_i2s(-ida->zoom,eb->x);
533 	    y = viewer_i2s(-ida->zoom,eb->y);
534             pix = ida_image_scanline(&ida->img, y) + x * 3;
535 	    ida->pick_cb(x,y,pix,ida->pick_data);
536 	    ida->pick_cb = NULL;
537 	    ida->pick_data = NULL;
538 	    ida->state = POINTER_NORMAL;
539 	    state = POINTER_NORMAL;
540 	    break;
541 	case RUBBER_NEW:
542 	    ida->mask = 0x33333333;
543 	    ida->current.x1 = ida->current.x2 = viewer_i2s(-ida->zoom,eb->x);
544 	    ida->current.y1 = ida->current.y2 = viewer_i2s(-ida->zoom,eb->y);
545 	    break;
546 	case RUBBER_MOVE:
547 	    ida->last_x = viewer_i2s(-ida->zoom,eb->x);
548 	    ida->last_y = viewer_i2s(-ida->zoom,eb->y);
549 	    break;
550 	case RUBBER_X1:
551 	    ida->current.x1 = viewer_i2s(-ida->zoom,eb->x);
552 	    break;
553 	case RUBBER_Y1:
554 	    ida->current.y1 = viewer_i2s(-ida->zoom,eb->y);
555 	    break;
556 	case RUBBER_X2:
557 	    ida->current.x2 = viewer_i2s(-ida->zoom,eb->x);
558 	    break;
559 	case RUBBER_Y2:
560 	    ida->current.y2 = viewer_i2s(-ida->zoom,eb->y);
561 	    break;
562 	}
563 	state = ida->state;
564 	break;
565     }
566     case MotionNotify:
567     {
568 	XMotionEvent *em = (XMotionEvent*)ev;
569 
570 	if (!(em->state & Button1Mask)) {
571 	    state = viewer_pos2state(ida,em->x,em->y);
572 	    goto out;
573 	}
574 	switch (ida->state) {
575 	case RUBBER_NEW:
576 	    ida->current.x2 = viewer_i2s(-ida->zoom,em->x);
577 	    ida->current.y2 = viewer_i2s(-ida->zoom,em->y);
578 	    if (em->state & ShiftMask) {
579 		/* square selection */
580 		int xlen,ylen;
581 		xlen = abs(ida->current.x1 - ida->current.x2);
582 		ylen = abs(ida->current.y1 - ida->current.y2);
583 		if (ylen > xlen) {
584 		    if (ida->current.x1 < ida->current.x2)
585 			ida->current.x2 -= (xlen - ylen);
586 		    else
587 			ida->current.x2 += (xlen - ylen);
588 		} else {
589 		    if (ida->current.y1 < ida->current.y2)
590 			ida->current.y2 -= (ylen - xlen);
591 		    else
592 			ida->current.y2 += (ylen - xlen);
593 		}
594 	    }
595 	    break;
596 	case RUBBER_MOVE:
597 	    x = viewer_i2s(-ida->zoom,em->x);
598 	    y = viewer_i2s(-ida->zoom,em->y);
599 	    ida->current.x1 += (x - ida->last_x);
600 	    ida->current.x2 += (x - ida->last_x);
601 	    ida->current.y1 += (y - ida->last_y);
602 	    ida->current.y2 += (y - ida->last_y);
603 	    ida->last_x = x;
604 	    ida->last_y = y;
605 	    break;
606 	case RUBBER_X1:
607 	    ida->current.x1 = viewer_i2s(-ida->zoom,em->x);
608 	    break;
609 	case RUBBER_Y1:
610 	    ida->current.y1 = viewer_i2s(-ida->zoom,em->y);
611 	    break;
612 	case RUBBER_X2:
613 	    ida->current.x2 = viewer_i2s(-ida->zoom,em->x);
614 	    break;
615 	case RUBBER_Y2:
616 	    ida->current.y2 = viewer_i2s(-ida->zoom,em->y);
617 	    break;
618 	}
619 	state = ida->state;
620 	break;
621     }
622     case ButtonRelease:
623     {
624 	XButtonEvent *eb = (XButtonEvent*)ev;
625 
626 	if (eb->button != Button1)
627 	    goto out;
628 	ida->state = POINTER_NORMAL;
629 	state = ida->state;
630 	break;
631     }
632     }
633 
634     if (ida->current.x1 < 0)
635 	ida->current.x1 = 0;
636     if (ida->current.x1 > ida->img.i.width)
637 	ida->current.x1 = ida->img.i.width;
638     if (ida->current.x2 < 0)
639 	ida->current.x2 = 0;
640     if (ida->current.x2 > ida->img.i.width)
641 	ida->current.x2 = ida->img.i.width;
642     if (ida->current.y1 < 0)
643 	ida->current.y1 = 0;
644     if (ida->current.y1 > ida->img.i.height)
645 	ida->current.y1 = ida->img.i.height;
646     if (ida->current.y2 < 0)
647 	ida->current.y2 = 0;
648     if (ida->current.y2 > ida->img.i.height)
649 	ida->current.y2 = ida->img.i.height;
650 
651  out:
652     XDefineCursor(dpy, XtWindow(widget), ptrs[state]);
653     viewer_rubber_on(ida);
654 }
655 
656 /* ----------------------------------------------------------------------- */
657 /* public stuff                                                            */
658 
viewer_pick(struct ida_viewer * ida,viewer_pick_cb cb,XtPointer data)659 void viewer_pick(struct ida_viewer *ida, viewer_pick_cb cb, XtPointer data)
660 {
661     if (POINTER_NORMAL != ida->state)
662 	return;
663     if (debug)
664 	fprintf(stderr,"viewer_pick\n");
665     ida->state = POINTER_PICK;
666     ida->pick_cb   = cb;
667     ida->pick_data = data;
668 }
669 
viewer_unpick(struct ida_viewer * ida)670 void viewer_unpick(struct ida_viewer *ida)
671 {
672     if (POINTER_PICK != ida->state)
673 	return;
674     if (debug)
675 	fprintf(stderr,"viewer_unpick\n");
676     ida->state = POINTER_NORMAL;
677     ida->pick_cb   = NULL;
678     ida->pick_data = NULL;
679 }
680 
681 void
viewer_autozoom(struct ida_viewer * ida)682 viewer_autozoom(struct ida_viewer *ida)
683 {
684     if (GET_AUTOZOOM()) {
685 	ida->zoom = 0;
686 	while (XtScreen(ida->widget)->width  < viewer_i2s(ida->zoom,ida->img.i.width) ||
687 	       XtScreen(ida->widget)->height < viewer_i2s(ida->zoom,ida->img.i.height))
688 	    ida->zoom--;
689     }
690     viewer_new_view(ida);
691 }
692 
693 void
viewer_setzoom(struct ida_viewer * ida,int zoom)694 viewer_setzoom(struct ida_viewer *ida, int zoom)
695 {
696     ida->zoom = zoom;
697     viewer_new_view(ida);
698 }
699 
700 static void
viewer_op_rect(struct ida_viewer * ida)701 viewer_op_rect(struct ida_viewer *ida)
702 {
703     if (ida->current.x1 == ida->current.x2 &&
704 	ida->current.y1 == ida->current.y2) {
705 	/* full image */
706 	ida->op_rect.x1 = 0;
707 	ida->op_rect.x2 = ida->img.i.width;
708 	ida->op_rect.y1 = 0;
709 	ida->op_rect.y2 = ida->img.i.height;
710 	return;
711     } else {
712 	/* have selection */
713 	if (ida->current.x1 < ida->current.x2) {
714 	    ida->op_rect.x1 = ida->current.x1;
715 	    ida->op_rect.x2 = ida->current.x2;
716 	} else {
717 	    ida->op_rect.x1 = ida->current.x2;
718 	    ida->op_rect.x2 = ida->current.x1;
719 	}
720 	if (ida->current.y1 < ida->current.y2) {
721 	    ida->op_rect.y1 = ida->current.y1;
722 	    ida->op_rect.y2 = ida->current.y2;
723 	} else {
724 	    ida->op_rect.y1 = ida->current.y2;
725 	    ida->op_rect.y2 = ida->current.y1;
726 	}
727     }
728 }
729 
730 int
viewer_start_op(struct ida_viewer * ida,struct ida_op * op,void * parm)731 viewer_start_op(struct ida_viewer *ida, struct ida_op *op, void *parm)
732 {
733     struct ida_image dst;
734 
735     ptr_busy();
736     viewer_workfinish(ida);
737     viewer_rubber_off(ida);
738 
739     /* try init */
740     viewer_op_rect(ida);
741     if (debug)
742 	fprintf(stderr,"viewer_start_op: init %s(%p)\n",op->name,parm);
743     memset(&dst, 0, sizeof(dst));
744     ida->op_data = op->init(&ida->img,&ida->op_rect,&dst.i,parm);
745     ptr_idle();
746     if (NULL == ida->op_data)
747 	return -1;
748     ida_image_alloc(&dst);
749 
750     /* prepare background processing */
751     if (ida->undo.p) {
752 	ida_image_free(&ida->undo);
753 	memset(&ida->undo,0,sizeof(ida->undo));
754     }
755     if (ida->op_src.p) {
756 	fprintf(stderr,"have op_src buffer /* shouldn't happen */");
757 	ida_image_free(&ida->op_src);
758     }
759     ida->op_src = ida->img;
760     ida->img = dst;
761     ida->op_line = 0;
762     ida->op_work = op->work;
763     ida->op_done = op->done;
764     ida->op_preview = 0;
765 
766     if (ida->op_src.i.width  != ida->img.i.width ||
767 	ida->op_src.i.height != ida->img.i.height) {
768 	memset(&ida->current,0,sizeof(ida->current));
769 	viewer_autozoom(ida);
770     } else
771 	viewer_new_view(ida);
772     return 0;
773 }
774 
775 int
viewer_undo(struct ida_viewer * ida)776 viewer_undo(struct ida_viewer *ida)
777 {
778     int resize;
779 
780     viewer_workfinish(ida);
781     if (NULL == ida->undo.p)
782 	return -1;
783     viewer_rubber_off(ida);
784     memset(&ida->current,0,sizeof(ida->current));
785 
786     resize = (ida->undo.i.width  != ida->img.i.width ||
787 	      ida->undo.i.height != ida->img.i.height);
788     ida_image_free(&ida->img);
789     ida->img = ida->undo;
790     memset(&ida->undo,0,sizeof(ida->undo));
791 
792     if (resize)
793 	viewer_autozoom(ida);
794     else
795 	viewer_new_view(ida);
796     return 0;
797 }
798 
799 int
viewer_start_preview(struct ida_viewer * ida,struct ida_op * op,void * parm)800 viewer_start_preview(struct ida_viewer *ida, struct ida_op *op, void *parm)
801 {
802     struct ida_image dst;
803 
804     viewer_workfinish(ida);
805 
806     /* try init */
807     viewer_op_rect(ida);
808     ida->op_data = op->init(&ida->img,&ida->op_rect,&dst.i,parm);
809     if (NULL == ida->op_data)
810 	return -1;
811 
812     /* prepare background preview */
813     ida->op_line = 0;
814     ida->op_work = op->work;
815     ida->op_done = op->done;
816     ida->op_preview = 1;
817 
818     viewer_workstart(ida);
819     return 0;
820 }
821 
822 int
viewer_cancel_preview(struct ida_viewer * ida)823 viewer_cancel_preview(struct ida_viewer *ida)
824 {
825     viewer_workstop(ida);
826     viewer_workstart(ida);
827     return 0;
828 }
829 
830 int
viewer_loader_start(struct ida_viewer * ida,struct ida_loader * loader,FILE * fp,char * filename,unsigned int page)831 viewer_loader_start(struct ida_viewer *ida, struct ida_loader *loader,
832 		    FILE *fp, char *filename, unsigned int page)
833 {
834     struct ida_image_info info;
835     void *data;
836 
837     /* init loader */
838     ptr_busy();
839     memset(&info,0,sizeof(info));
840     data = loader->init(fp,filename,page,&info,0);
841     ptr_idle();
842     if (NULL == data) {
843 	fprintf(stderr,"loading %s [%s] FAILED\n",filename,loader->name);
844 	if (fp)
845 	    hex_display(filename);
846 	return -1;
847     }
848 
849     /* ok, going to load new image */
850     viewer_workstop(ida);
851     viewer_rubber_off(ida);
852     memset(&ida->current,0,sizeof(ida->current));
853     if (ida->undo.p) {
854 	ida_image_free(&ida->undo);
855 	memset(&ida->undo,0,sizeof(ida->undo));
856     }
857     if (NULL != ida->img.p)
858         ida_image_free(&ida->img);
859     ida->file       = filename;
860     ida->img.i      = info;
861     ida_image_alloc(&ida->img);
862 
863     /* prepare background loading */
864     ida->load_line = 0;
865     ida->load_read = loader->read;
866     ida->load_done = loader->done;
867     ida->load_data = data;
868 
869     viewer_autozoom(ida);
870     return info.npages;
871 }
872 
873 int
viewer_loadimage(struct ida_viewer * ida,char * filename,unsigned int page)874 viewer_loadimage(struct ida_viewer *ida, char *filename, unsigned int page)
875 {
876     struct list_head  *item;
877     struct ida_loader *loader;
878     char blk[512];
879     FILE *fp;
880 
881     if (NULL == (fp = fopen(filename, "r"))) {
882 	fprintf(stderr,"fopen %s: %s\n",filename,strerror(errno));
883         return -1;
884     }
885     if (debug)
886 	fprintf(stderr,"load: %s\n",filename);
887     memset(blk,0,sizeof(blk));
888     fread(blk,1,sizeof(blk),fp);
889     rewind(fp);
890 
891     /* pick loader */
892     list_for_each(item,&loaders) {
893         loader = list_entry(item, struct ida_loader, list);
894 #if 0
895 	if (NULL == loader->magic)
896 	    break;
897 #else
898 	if (NULL == loader->magic)
899 	    continue;
900 #endif
901 	if (0 == memcmp(blk+loader->moff,loader->magic,loader->mlen))
902 	    return viewer_loader_start(ida,loader,fp,filename,page);
903     }
904     fprintf(stderr,"%s: unknown format\n",filename);
905     hex_display(filename);
906     fclose(fp);
907     return -1;
908 }
909 
910 int
viewer_setimage(struct ida_viewer * ida,struct ida_image * img,char * name)911 viewer_setimage(struct ida_viewer *ida, struct ida_image *img, char *name)
912 {
913     /* ok, going to load new image */
914     viewer_workstop(ida);
915     viewer_rubber_off(ida);
916     memset(&ida->current,0,sizeof(ida->current));
917     if (ida->undo.p) {
918 	ida_image_free(&ida->undo);
919 	memset(&ida->undo,0,sizeof(ida->undo));
920     }
921 
922     if (NULL != ida->img.p)
923 	ida_image_free(&ida->img);
924     ida->file       = name;
925     ida->img        = *img;
926 
927     viewer_autozoom(ida);
928     return 0;
929 }
930 
931 struct ida_viewer*
viewer_init(Widget widget)932 viewer_init(Widget widget)
933 {
934     Colormap cmap = DefaultColormapOfScreen(XtScreen(widget));
935     struct ida_viewer *ida;
936     XColor white,red,dummy;
937     unsigned int i;
938 
939     ida = malloc(sizeof(*ida));
940     memset(ida,0,sizeof(*ida));
941     ida->widget = widget;
942     XtAddEventHandler(widget,ExposureMask,False,viewer_redraw,ida);
943     XtAddEventHandler(widget,
944 		      ButtonPressMask   |
945 		      ButtonReleaseMask |
946 		      PointerMotionMask,
947 		      False,viewer_mouse,ida);
948 
949     ptrs[POINTER_NORMAL] = XCreateFontCursor(dpy,XC_left_ptr);
950     ptrs[POINTER_BUSY]   = XCreateFontCursor(dpy,XC_watch);
951     ptrs[POINTER_PICK]   = XCreateFontCursor(dpy,XC_tcross);
952     ptrs[RUBBER_NEW]     = XCreateFontCursor(dpy,XC_left_ptr);
953     ptrs[RUBBER_MOVE]    = XCreateFontCursor(dpy,XC_fleur);
954     ptrs[RUBBER_X1]      = XCreateFontCursor(dpy,XC_sb_h_double_arrow);
955     ptrs[RUBBER_X2]      = XCreateFontCursor(dpy,XC_sb_h_double_arrow);
956     ptrs[RUBBER_Y1]      = XCreateFontCursor(dpy,XC_sb_v_double_arrow);
957     ptrs[RUBBER_Y2]      = XCreateFontCursor(dpy,XC_sb_v_double_arrow);
958     if (XAllocNamedColor(dpy,cmap,"white",&white,&dummy) &&
959 	XAllocNamedColor(dpy,cmap,"red",&red,&dummy))
960 	for (i = 0; i < sizeof(ptrs)/sizeof(Cursor); i++)
961 	    XRecolorCursor(dpy,ptrs[i],&red,&white);
962 
963     return ida;
964 }
965