1 
2 #include "global.h"
3 
4 #include <signal.h>
5 #ifdef PINUP_DEFAULT
6 #	include <sys/stat.h>
7 #	include <pwd.h>
8 #endif
9 #include <X11/Xlib.h>
10 #include <X11/Xutil.h>
11 #include <X11/keysym.h>
12 #include <X11/cursorfont.h>
13 
14 #include <X11/extensions/shape.h>
15 
16 #include <X11/Xos.h>
17 #ifndef X_GETTIMEOFDAY
18 	/* define X_GETTIMEOFDAY macro, a portable gettimeofday() */
19 	/* copied from Xos.h for pre-X11R6 releases               */
20 #	if defined(SVR4) || defined(VMS) || defined(WIN32)
21 #		define X_GETTIMEOFDAY(t) gettimeofday(t)
22 #	else
23 #		define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0)
24 #	endif
25 #endif
26 #ifndef FDS_TYPE
27 //
28 // The problem occured, that on HP-UX, the type used in the masks of the
29 // select() system call are not the usual 'fd_set'. Instead they are just
30 // integer pointers, which leads to an compile-error, when not casted.
31 // Therefore it actually will get casted by the following Macro FDS_TYPE,
32 // which should usually be defined to an empty string.
33 //
34 #  ifdef __hpux
35 #     define   FDS_TYPE (int*)
36 #  else
37 #     define   FDS_TYPE
38 #  endif
39 #endif
40 
41 #ifndef _objects_h
42 #	include "objects.H"
43 #endif
44 
45 #include "gifx_image.H"
46 #include "color_mapper.H"
47 #include "imgbuff.H"
48 #include "puzzle.H"
49 
50 #include "cursor.h"
51 
52 #define	_DEBUG
53 #include "global.h"
54 
55 #ifndef JIG_DEFAULT
56 #	define JIG_DEFAULT	"default.gif"
57 #endif
58 
59 Display	*dpy;
60 int		scr;
61 Window	win;
62 GC			gc;
63 
64 int pixmap_depth = 0;
65 int scanline_pad = 0;
66 
67 Cursor	normal_cursor, move_cursor, pull_cursor, idle_cursor, no_cursor;
68 
69 int		zoom_factor=20;
70 int		win_size_x=100;
71 int		win_size_y=100;
72 
73 int		offx=0;					// half tilesize as offset to frames
74 int		offy=0;
75 int		width =0;				// height of image
76 int		height=0;				// width of image
77 int		dx = 0;					// number of tiles in x-direction
78 int		dy = 0;					// number of tiles in y-direction
79 int		tile_size=0;			// average tile size
80 int		shuffle=3;				// shuffle tile as default
81 int		side_lock=-1;			// which side (of TwinPixmap) as default
82 #ifdef __hpux
83 int		shared=0;				// dont use extension as default
84 #else
85 int		shared=1;				// use MIT-SHM extension as default
86 #endif
87 int		shapes=0;
88 
89 int		quit=0;					// global flag to initiate quitting
90 int		random_seed=0;
91 int		distortion=20;			// factor to control distortion of the tiles
92 double	maxang=45;				// maximum rotation angle (off from 90degrees)
93 int		shadow_size=1;			// pixels in shadow frame
94 int		straight_setup=-1;	// offset for straight debugging setup
95 int		no_flip=0;				// suppress automatic flip of landscape images
96 int		autocrop=1;				// try to cut the edges away
97 
98 double	fliptimebase=0.07;	// base time for flipping
99 double	fliptimedelta=0.02;	// added to base for each tile
100 int		maxfliptiles=8;		// max. number of tiles for automated flip
101 int		minadjustcount=4;		// number of tiles to start 90 degrees autoadjust
102 double	flipsave=0.2;			// dont let the tile come close to a vertical
103                               // position during the flip ...
104 
105 double	turntimebase=0.15;	// base time for 90 degree rotation
106 double	turntimedelta=0.02;	// added to base for each additional tile
107 int		maxturntiles=5;		// max. number of tiles for rotation animation
108 
109 int		maxsnapretries=1;		// max. no. of tiles that could recursively snap together
110 
111 int		warp_center;
112 int		warp_lock_x=WARP_NO_LOCK;
113 int		warp_lock_y=WARP_NO_LOCK;
114 
115 char		*dispname="";
116 char		*filename=JIG_DEFAULT;
117 int		verbose=0;
118 int		rotate=0;				// rotation demo for debugging
119 int		angle=0;					// preset angles for debugging
120 
121 class Puzzle		*p;			// Collection of all puzzle pieces
122 class GifPixmap	*pm;			// Original pixmap for the puzzle tiles
123 class Port			*port;		// Port (Display synonym) for color mapping
124 class ObjectStack	*stk;			// administrator object for all viewable objects
125 class ImageBuffer	*img_buf;	//	memory for rotating image (probably shared)
126 
127 // ===========================================================================
128 
129 static unsigned char b[2];
130 
my_srand(unsigned seed)131 static void my_srand( unsigned seed ) {
132 	b[0]=seed&0xff;
133 	b[1]=seed>>8;
134 }
135 
my_rand(void)136 int my_rand(void) {
137 	unsigned long	s1=b[0]<<8|b[1];
138 	s1+=12345;
139 	s1*=s1;
140 	b[0]=(unsigned char)((s1>>8)&0xff);
141 	b[1]=(unsigned char)((s1>>16)&0xff);
142 	return (s1>>8)&0xffff;
143 }
144 
145 // ===========================================================================
146 
local_usleep(long time)147 static void local_usleep( long time )
148 {
149 struct timeval  timeout;
150 int    nfound;
151 
152    timeout.tv_sec  = (long)0;
153    timeout.tv_usec = (long)time;
154 
155    nfound=select(0,0,0,0,&timeout);
156 }
157 
do_sound(char * str)158 /*static*/ void do_sound( char *str ) {
159 XKeyboardState		old_keyboard_values;
160 XKeyboardControl	values;
161 int		pitch, percent,duration,pause;
162 char		*str_p=str;
163 
164 	XGetKeyboardControl(dpy,&old_keyboard_values);
165 	while( sscanf(str_p,"%03d%02d%02d%02d;",&pitch,&percent,&duration,&pause)==4 ) {
166 		values.bell_pitch=pitch;
167 		values.bell_percent=percent;
168 		values.bell_duration=duration;
169 		XChangeKeyboardControl(dpy,KBBellPercent|KBBellPitch|KBBellDuration,&values);
170 		XBell(dpy,values.bell_percent);
171 		XFlush(dpy);
172 		local_usleep(pause*10000);
173 		str_p+=10;
174 	}
175 #ifdef __hpux
176 	values.bell_pitch    = old_keyboard_values.bell_pitch;
177 	values.bell_percent  = old_keyboard_values.bell_percent;
178 	values.bell_duration = old_keyboard_values.bell_duration;
179 #else
180 	values.bell_pitch    = 440;
181 	values.bell_percent  =  50;
182 	values.bell_duration = 100;
183 #endif
184 	XChangeKeyboardControl(dpy,KBBellPercent|KBBellPitch|KBBellDuration,&values);
185 	XFlush(dpy);
186 }
187 
188 // ===========================================================================
189 //
190 // some help routines for easy drawing ...
191 //
192 
193 
DrawLine(const Real & x1,const Real & y1,const Real & x2,const Real & y2)194 void DrawLine( const Real& x1, const Real& y1, const Real& x2, const Real& y2 ) {
195 int   px1 = XPix(x1);
196 int   py1 = YPix(y1);
197 int   px2 = XPix(x2);
198 int   py2 = YPix(y2);
199 	XDrawLine( dpy, win, gc, px1, py1, px2, py2 );
200 }
DrawLine(const Vec2 & p1,const Vec2 & p2)201 inline void DrawLine( const Vec2 &p1, const Vec2 &p2 ) {
202    DrawLine( p1.X(), p1.Y(), p2.X(), p2.Y() );
203 }
204 
205 // ===========================================================================
206 
207 static unsigned long start_seconds;
208 
InitTime()209 static void InitTime() {
210 struct timeval start;
211 
212 	X_GETTIMEOFDAY( &start );
213 	start_seconds = start.tv_sec;
214 }
215 
GetCurrentTime(int busy)216 double GetCurrentTime(int busy) {
217 struct timeval current;
218 
219 	 X_GETTIMEOFDAY( &current );
220 #ifdef PINUP_DEFAULT
221 	 	static unsigned long last=0;
222 	 	static unsigned long idle_start=0;
223 
224 	 	unsigned long	val=(current.tv_sec%86400uL);
225 	 	if (busy)	idle_start=val+15;
226 	 	if (last!=val/5&&val<idle_start) {
227 	 		last=val/5;
228 	 		unsigned long	minute=val/60+120;
229 	 		if (minute<540||(minute>=570&&minute<765)||(minute>=810&&minute<1020)) {
230 				do_sound( "440995005;370995005;440995005;" );
231 			}
232 	 	}
233 #else
234 	(void)busy;
235 #endif
236 	 return( ((double)(current.tv_sec-start_seconds))+(current.tv_usec/1000000.0) );
237 }
238 
239 // ===========================================================================
240 
241 class BackDrop : public Object {
242 	public:
243 		BackDrop();
244 		virtual ~BackDrop();
245 
246 		virtual void ExposeRegion( int x, int y, int width, int height );
247 		virtual void ZoomView( int midx, int midy, int chg );
248 
249 	protected:
250 		void InitFillStyle();
251 
252 		unsigned long	toppixel;
253 		unsigned long	bgpixel;
254 		unsigned long	botpixel;
255 		Pixmap	tilemap;
256 		GC			gc_tile;
257 };
258 
BackDrop()259 BackDrop::BackDrop() {
260 	bgpixel =port->AllocNamedColor( "grey50" );
261 	toppixel=port->AllocNamedColor( "grey60" );
262 	botpixel=port->AllocNamedColor( "grey40" );
263 
264 	gc_tile=XCreateGC(dpy,RootWindow(dpy,scr),0,0);
265 	InitFillStyle();
266 }
267 
~BackDrop()268 BackDrop::~BackDrop() {
269 	XFreeGC(dpy,gc_tile);
270 	XFreePixmap(dpy,tilemap);
271 }
272 
InitFillStyle()273 void BackDrop::InitFillStyle() {
274 	tilemap=XCreatePixmap(dpy,RootWindow(dpy,scr),
275 								zoom_factor,zoom_factor,DefaultDepth(dpy,scr));
276 	XSetForeground(dpy,DefaultGC(dpy,scr),bgpixel);
277 	XFillRectangle(dpy,tilemap,DefaultGC(dpy,scr),0,0,zoom_factor,zoom_factor);
278 	XSetForeground(dpy,DefaultGC(dpy,scr),toppixel);
279 	XDrawLine(dpy,tilemap,DefaultGC(dpy,scr),0,0,zoom_factor-2,0);
280 	XDrawLine(dpy,tilemap,DefaultGC(dpy,scr),0,1,0,zoom_factor-2);
281 	XSetForeground(dpy,DefaultGC(dpy,scr),botpixel);
282 	XDrawLine(dpy,tilemap,DefaultGC(dpy,scr),zoom_factor-1,0,zoom_factor-1,zoom_factor-1);
283 	XDrawLine(dpy,tilemap,DefaultGC(dpy,scr),0,zoom_factor-1,zoom_factor-2,zoom_factor-1);
284 	XSetTile(dpy,gc_tile,tilemap);
285 	XSetFillStyle(dpy,gc_tile,FillTiled);
286 }
287 
ExposeRegion(int x,int y,int width,int height)288 void BackDrop::ExposeRegion( int x, int y, int width, int height ) {
289 	XSetTSOrigin(dpy,gc_tile,-x,-y);
290 	XFillRectangle(dpy,mystack->dbmap,gc_tile,0,0,width,height);
291 }
292 
ZoomView(int,int,int chg)293 void BackDrop::ZoomView( int /*midx*/, int /*midy*/, int chg ) {
294 	zoom_factor+=chg;
295 	XFreePixmap(dpy,tilemap);
296 	InitFillStyle();
297 	zoom_factor-=chg;
298 }
299 
300 // ===========================================================================
301 
usage()302 void usage() {
303 	printf( "usage  : xjig [<options>]\n" );
304 	printf( "\n" );
305 	printf( "options: -file <file>: use gif-image in <file>\n" );
306 	printf( "         -w  <n>     : number of tiles in x direction\n" );
307 	printf( "         -h  <n>     : number of tiles in y direction\n" );
308 	printf( "         -ts <n>     : set average tile size\n" );
309 	printf( "         -ww <n>     : width of image\n" );
310 	printf( "         -wh <n>     : height of image\n" );
311 	printf( "         -side <n>   : lock side <n> only\n" );
312 	printf( "         -no_flip    : no automatic flip of landscape images\n" );
313 	printf( "         -no_crop    : do not crop images\n" );
314 #ifdef USE_MIT_SHM
315 	printf( "         -shm        : use MIT-SHM %s\n", (shared)?"(default)":"" );
316 	printf( "         -no_shm     : don't use MIT-SHM extension %s\n", (shared)?"":"(default)" );
317 #endif
318 	printf( "         -shapes     : use shape extension to draw on desktop\n" );
319 	printf( "         -no_anim    : don't animate rotation and flipping of tiles\n" );
320 	if (verbose) {
321 	printf( "additional options for debugging:\n" );
322 	printf( "         -a  <n>    : startup angle\n" );
323 	printf( "         -s         : shuffle tiles\n" );
324 	printf( "         -sf        : full shuffle\n" );
325 	printf( "         -sa        : shuffle angles\n" );
326 	printf( "         -sp        : shuffle positions\n" );
327 	printf( "         -r         : rotation demo\n" );
328 	printf( "         -8 -16 -24 -32 : manually select optimized texture mapping routine\n" );
329 	printf( "         -dist <n>  : distortion percentage\n" );
330 	printf( "         -maxang <n>: maximum rotation angle at startup\n" );
331 	printf( "         -rand <n>  : seed for random generator\n" );
332 	printf( "         -shadow <n>: pixels of shadowed border\n" );
333 	printf( "         -setup <n> : straight setup with offset <n>\n" );
334 	printf( "         -ftb <t>   : set flip time base                        (%g)\n", fliptimebase );
335 	printf( "         -ftd <t>   : set flip time delta (for aditional tiles) (%g)\n", fliptimedelta );
336 	printf( "         -mft <t>   : maximun number of flip tiles              (%d)\n", maxfliptiles );
337 	printf( "         -fs <t>    : set value, when to stop the flip          (%g)\n", flipsave );
338 	printf( "         -ttb <t>   : set turn time base                        (%g)\n", turntimebase );
339 	printf( "         -ttd <t>   : set turn time delta (for aditional tiles) (%g)\n", turntimedelta );
340 	printf( "         -mtt <t>   : maximun number of flip tiles              (%d)\n", maxturntiles );
341 	printf( "         -msr <t>   : maximun number snap retries               (%d)\n", maxsnapretries );
342 	printf( "         -mac <t>   : minimum number of tiles for adjustment    (%d)\n", minadjustcount );
343 	}
344 	printf( "\ncontrols:\n" );
345 	printf( "\n" );
346 	printf( "click left:              rotate 90 degrees left\n" );
347 	printf( "click right:             rotate 90 degrees right\n" );
348 	printf( "click middle:            flip tile to backside on double sided puzzle\n" );
349 	printf( "drag left:               drag with automatic rotation\n" );
350 	printf( "drag middle:             straight drag\n" );
351 	printf( "drag left+middle:        pause rotator drag for a straight drag\n" );
352 	printf( "drag middle+left:        pause straight drag for a static rotation\n" );
353 	printf( "drag middle+click left:  rotate 90 degrees left during straight drag\n" );
354 	printf( "drag middle+click right: rotate 90 degrees right during straight drag\n" );
355 	printf( "CTRL + click left:       (same as click middle)\n" );
356 
357 	printf( "\n" );
358 	printf( "author : Helmut Hoenig, July-24-96 (V2.4)   (Helmut.Hoenig@hub.de)\n" );
359 	printf( "\n" );
360 	exit(0);
361 }
362 
scan_args(int argc,char ** argv)363 void scan_args( int argc, char **argv ) {
364 	for (int i=1;i<argc;i++) {
365 		if (!strcmp(argv[i],"-display"))			dispname=argv[++i];
366 		else if (!strcmp(argv[i],"-ww"))	width =atoi(argv[++i]);
367 		else if (!strcmp(argv[i],"-wh")||!strcmp(argv[i],"-hh"))	height=atoi(argv[++i]);
368 		else if (!strcmp(argv[i],"-w"))	dx=atoi(argv[++i]);
369 		else if (!strcmp(argv[i],"-h")&&i<argc-1)	dy=atoi(argv[++i]);
370 		else if (!strcmp(argv[i],"-ts"))			tile_size=atoi(argv[++i]);
371 		else if (!strcmp(argv[i],"-r"))			rotate=1;
372 		else if (!strcmp(argv[i],"-a")) {
373 				angle=atoi(argv[++i]);
374 				shuffle&=~1;
375 		}
376 		else if (!strcmp(argv[i],"-s"))			shuffle=0;
377 
378 		else if (!strcmp(argv[i],"-sf"))			shuffle|=4;
379 		else if (!strcmp(argv[i],"-sa"))			shuffle=1;
380 		else if (!strcmp(argv[i],"-sp"))			shuffle=2;
381 		else if (!strcmp(argv[i],"-no_flip"))  no_flip=1;
382 		else if (!strcmp(argv[i],"-no_crop"))  autocrop=0;
383 		else if (!strcmp(argv[i],"-side"))		side_lock=atoi(argv[++i]);
384 		else if (!strcmp(argv[i],"-rand"))		random_seed=atoi(argv[++i]);
385 		else if (!strcmp(argv[i],"-dist"))		distortion=atoi(argv[++i]);
386 		else if (!strcmp(argv[i],"-maxang"))	maxang=atof(argv[++i]);
387 		else if (!strcmp(argv[i],"-shadow"))	shadow_size=atoi(argv[++i]);
388 		else if (!strcmp(argv[i],"-setup")) {
389 				straight_setup=atoi(argv[++i]);
390 				shuffle=0;
391 		}
392 		else if (!strcmp(argv[i],"-file"))			filename=argv[++i];
393 #ifdef PINUP_DEFAULT
394 		else if (!strcmp(argv[i],"-pinup"))			filename=PINUP_DEFAULT;
395 #endif
396 		else if (!strcmp(argv[i],"-8"))				pixmap_depth=8;
397 		else if (!strcmp(argv[i],"-16"))				pixmap_depth=16;
398 		else if (!strcmp(argv[i],"-32"))				pixmap_depth=32;
399 		else if (!strcmp(argv[i],"-24"))				pixmap_depth=24;
400 		else if (!strcmp(argv[i],"-shm"))			shared=1;
401 		else if (!strcmp(argv[i],"-no_shm"))		shared=0;
402 		else if (!strcmp(argv[i],"-shapes"))		shapes=1;
403 		else if (!strncmp(argv[i],"-verbose",2))	verbose=1;
404 		else if (!strcmp(argv[i],"-ftb"))			fliptimebase=atof(argv[++i]);
405 		else if (!strcmp(argv[i],"-ftd"))			fliptimedelta=atof(argv[++i]);
406 		else if (!strcmp(argv[i],"-mft"))			maxfliptiles=atoi(argv[++i]);
407 		else if (!strcmp(argv[i],"-fs"))				flipsave=atof(argv[++i]);
408 		else if (!strcmp(argv[i],"-ttb"))			turntimebase=atof(argv[++i]);
409 		else if (!strcmp(argv[i],"-ttd"))			turntimedelta=atof(argv[++i]);
410 		else if (!strcmp(argv[i],"-mtt"))			maxturntiles=atoi(argv[++i]);
411 		else if (!strcmp(argv[i],"-msr"))			maxsnapretries=atoi(argv[++i]);
412 		else if (!strcmp(argv[i],"-mac"))			minadjustcount=atoi(argv[++i]);
413 		else if (!strcmp(argv[i],"-no_anim")) {
414 									maxturntiles = maxfliptiles = 0;
415 		}
416 
417 		else usage();
418 	}
419 }
420 
421 void (*old_sighandler)(int);
sig_quit(int)422 void sig_quit( int ) {
423 	// handler that raises the quit flag on interrupts
424 	quit=1;
425 }
426 
427 static XErrorHandler old_handler;
error_handler(Display * dpy_in,XErrorEvent * xerror)428 int error_handler( Display * dpy_in, XErrorEvent *xerror ) {
429 	if (xerror->error_code==BadWindow) {
430 		// just ignore BadWindow failures, since they easily might occur
431 		// due to the destruction of windows.
432 		return 0;
433 	}
434 	else return old_handler( dpy_in, xerror );
435 }
436 
main(int argc,char ** argv)437 int main(int argc, char **argv)
438 {
439 static char *def_argv[]=
440  {	"xpuzzle", "-file", JIG_DEFAULT, 0 };
441 XEvent	event;
442 unsigned long next_sec=0;
443 const		char *options;
444 
445 	if (argc<2) {
446 		argv=def_argv;
447 		argc=(sizeof(def_argv)/sizeof(char*))-1;
448 	}
449 
450 	scan_args( argc, argv );
451 
452 	quit=0;
453 	old_sighandler=signal( SIGINT, sig_quit );
454 
455 	//
456 	// open the display and a port object as a color manager
457 	//
458 	dpy = XOpenDisplay(dispname);
459 	if (!dpy) {
460 		fprintf(stderr,"can't open display '%s'\n", dispname);
461 		exit(-1);
462 	}
463 	scr = DefaultScreen(dpy);
464 	port = new Port(dpy);
465 
466 	printf( "\n" );
467 	printf( "xjig V2.4, by Helmut Hoenig, July-24-96\n" );
468 	printf( "\n" );
469 
470 	XPixmapFormatValues *pmf;
471 
472 	int n;
473 	pmf = XListPixmapFormats (dpy, &n);
474 	if (pmf) {
475 		for (int i = 0; i < n; i++) {
476 			if (pixmap_depth) {
477 				if (pixmap_depth == pmf[i].depth) {
478 					scanline_pad = pmf[i].scanline_pad;
479 					break;
480 				}
481 			} else if (pmf[i].depth == DefaultDepth(dpy,scr)) {
482 				pixmap_depth = pmf[i].depth;
483 				scanline_pad = pmf[i].scanline_pad;
484 				break;
485 			}
486 		}
487 		XFree ((char *) pmf);
488 	}
489 
490 
491 	if (!scanline_pad) {
492 		fprintf(stderr,"No matching depth mode found\n");
493 		exit(1);
494 	}
495 
496 	if (verbose) {
497 		printf("depth: %d  pad: %d\n",pixmap_depth,scanline_pad);
498 	}
499 
500 	old_handler=XSetErrorHandler( error_handler );
501 
502 	// check for shapes
503 	if (shapes) {
504 		int major, minor;
505 		shapes=XShapeQueryVersion(dpy,&major,&minor);
506 		if (shapes&verbose)
507 			printf( "--- using shape extension V%d.%d\n", major, minor );
508 	}
509 
510 #ifdef PINUP_DEFAULT
511 	{
512 	struct stat buf;
513 	struct passwd *pwd;
514 	char	name[256];
515 
516 		stat( filename, &buf );
517 		pwd=getpwuid( buf.st_uid );
518 		strcpy( name, pwd->pw_gecos );
519 		if (strchr(name,','))		*strchr(name,',')='\0';
520 		if ( !strstr( filename, "default" ) ) {
521 			if (!strcmp( name, "Gast-Kennung"))		strcpy(name,"Norbert Klaus");
522 			printf( "loading: '%s', supplied by %s.\n\n", filename, name );
523 		}
524 	}
525 #endif
526 
527 	//
528 	// load image an scale it according to the input options
529 	// or set original image size, when no size selected.
530 	//
531 	pm=new GifPixmap(port,filename);
532 	options=pm->GetExtensionData( OPTION_EXTENSION );
533 	if (options) {
534 		char	opt_buffer[256];
535 		int	argc_opt=0;
536 		char	*argv_opt[20];
537 		char	*cptr=opt_buffer;
538 		strcpy(opt_buffer,options);
539 
540 		argv_opt[argc_opt++]=argv[0];
541 		argv_opt[argc_opt++]=cptr;
542 		while( *cptr ) {
543 			if ( *cptr==' ' ) {
544 				*cptr++=0;
545 				argv_opt[argc_opt++]=cptr;
546 			}
547 			else cptr++;
548 		}
549 		scan_args( argc_opt, argv_opt );
550 	}
551 	const char *comment=pm->GetExtensionData( COMMENT_EXTENSION );
552 	if (comment)	{ printf( comment ); putchar( '\n' ); }
553 
554 	if (autocrop)		pm->CropImage();
555 	if (pm->Width()>pm->Height()&&!no_flip)		pm->Rotate90();
556 	if (verbose)
557 		printf("original image size: %d %d\n", pm->Width(), pm->Height() );
558 
559 	if (!width&&!height) {
560 		width = pm->Width();
561 		height= pm->Height();
562 	}
563 	else {
564 		// scale to desired size, (keep aspect when only one param selected)
565 		if (!width)		width=height*pm->Width()/pm->Height();
566 		if (!height)	height=width*pm->Height()/pm->Width();
567 	}
568 	pm->CreateData( width, height );
569 
570 	if (!dx||!dy) {
571 		if (dy)			dx=width*dy/height;
572 		else if (dx)	dy=height*dx/width;
573 		else if (tile_size) {
574 							dx=width/tile_size;
575 							dy=height/tile_size;
576 		}
577 	}
578 	if (dx<=0||dy<=0)	 { dx=4; dy=6; }
579 
580 	if (verbose)
581 		printf( "number of tiles: %d\n", dx * dy );
582 
583 	offy = height/dy/2;
584 	if ( (height+(dy+1)*offy) > DisplayHeight(dpy,scr)-20 ) {
585 		offy=(DisplayHeight(dpy,scr)-height-20)/(dy+1);
586 	}
587 	offx = width /dx/2;
588 	win_size_x=2*(width+dx*offx)+offx;
589 	if ( win_size_x > DisplayWidth(dpy,scr)-8 ) {
590 		win_size_x=DisplayWidth(dpy,scr)-8;
591 		if ( 2*width+dx*offx+offx > win_size_x )	offx=(win_size_x-2*width)/(dx+1);
592 	}
593 	tile_size=width/dx;
594 
595 	if (shapes) {
596 		win_size_x=DisplayWidth(dpy,scr);
597 		win_size_y=DisplayHeight(dpy,scr);
598 		win=RootWindow(dpy,scr);
599 	}
600 	else {
601 		win_size_y=height+(dy+1)*offy;
602 
603 		win=XCreateSimpleWindow(dpy,RootWindow(dpy,scr),
604 				0,0,win_size_x,win_size_y,2,WhitePixel(dpy,scr),
605 						port->AllocNamedColor( "grey50" ) );
606 		XStoreName(dpy,win,argv[0]);
607 	}
608 	gc =XCreateGC(dpy,win,0,0);
609 	XSetForeground(dpy,gc,WhitePixel(dpy,scr));
610 
611 	// prepare some cursors
612 XColor	white_col, black_col;
613 Pixmap	pixmap;
614 
615 	XParseColor(dpy,DefaultColormap(dpy,scr), "white", &white_col );
616 	XParseColor(dpy,DefaultColormap(dpy,scr), "black", &black_col );
617 
618 	normal_cursor = XCreateFontCursor( dpy, XC_top_left_arrow );
619 	move_cursor   = XCreateFontCursor( dpy, XC_fleur );
620 	pull_cursor   = XCreateFontCursor( dpy, XC_hand2 );
621 	idle_cursor   = XCreateFontCursor( dpy, XC_watch );
622 
623 	pixmap = XCreateBitmapFromData(dpy,RootWindow(dpy,scr),
624 					cursor_bits, cursor_width, cursor_height );
625 	no_cursor = XCreatePixmapCursor( dpy, pixmap, pixmap,
626 					&white_col, &black_col, cursor_x_hot, cursor_y_hot );
627 	XFreePixmap( dpy, pixmap );
628 
629 
630 
631 	InitTime();
632 
633 	// create Object-Stack with Background
634 	if (shapes) {
635 		stk = new WindowObjectStack();
636 	}
637 	else {
638 		stk = new DBObjectStack();
639 		stk->Append( new BackDrop() );
640 	}
641 
642 	// create buffer for faster image rotation
643 	img_buf = new ImageBuffer();
644 
645 	// initialize puzzle game
646 	my_srand( random_seed );
647 	p = new Puzzle();
648 	p->Init(width,height,dx,dy, pm->GetExtensionData( FLATTILE_EXTENSION ) );
649 
650 	// check for hidden pieces
651 	options=pm->GetExtensionData( REMOVETILE_EXTENSION );
652 	if (options) {
653 		const char	*cptr=options;
654 		while(*cptr) {
655 			int x,y;
656 			sscanf(cptr,"%02x%02x",&x,&y);
657 			cptr+=4;
658 			p->DropTile(x,y);
659 		}
660 	}
661 
662 	if (shapes) {
663 		XSelectInput(dpy,win,KeyPressMask);
664 	}
665 	else {
666 		XSelectInput(dpy,win, ExposureMask|StructureNotifyMask|KeyPressMask|
667 				ButtonPressMask|ButtonReleaseMask|PointerMotionMask);
668 		XMapRaised(dpy,win);
669 	}
670 
671 	while(!quit) {
672 
673 		if (!XPending(dpy)) {
674 			XSync(dpy,0);
675 			while (!XPending(dpy)) {
676 				struct timeval timeout;
677 				struct fd_set readfds;
678 				int		nfds;
679 
680 				FD_ZERO( &readfds );
681 				FD_SET(  ConnectionNumber(dpy), &readfds  );
682 				nfds = ConnectionNumber(dpy)+1;
683 
684 				timeout.tv_sec  = 0;
685 				double current_time=GetCurrentTime();
686 				timeout.tv_usec = (long)(1000000 * (1.0-(current_time-floor(current_time))));
687 				select( nfds, FDS_TYPE &readfds, 0, 0, &timeout );
688 
689 				current_time = GetCurrentTime();
690 				//	printf( "%g\n", current_time );
691 				if ((unsigned long)current_time>next_sec) {
692 					char	 buffer[20];
693 					next_sec = (unsigned long)current_time;
694 					if (!p->Finished()) {
695 						sprintf( buffer, "xpuzzle:  %02ld:%02ld",
696 							next_sec / 60, next_sec % 60 );
697 						if (!shapes)	XStoreName(dpy,win,buffer);
698 					}
699 					else {
700 						sprintf( buffer, "no more tiles left at:  %02ld:%02ld",
701 							next_sec / 60, next_sec % 60 );
702 						if (!shapes)	XStoreName(dpy,win,buffer);
703 						XBell(dpy,100);
704 						XFlush(dpy);
705 						next_sec=1000000;
706 					}
707 				}
708 			}
709 		}
710 
711 
712 		XNextEvent(dpy,&event);
713 		switch(event.type) {
714 		case KeyPress: {
715 			char				buffer=0;
716 			XComposeStatus	compose;
717 			KeySym			keysym;
718 			int				mult=(event.xkey.state&ShiftMask)?2:1;
719 
720 			XDefineCursor(dpy,win,idle_cursor);
721 			XFlush(dpy);
722 			XLookupString( (XKeyEvent*)&event, &buffer, 1, &keysym, &compose );
723 			switch( keysym ) {
724 			case XK_plus:
725 			case XK_KP_Add:
726 			case XK_Page_Up:
727 			case XK_KP_Page_Up:
728 					stk->ZoomView(win_size_x/2,win_size_y/2,2*mult);
729 					stk->ExposeRegion(0,0,win_size_x,win_size_y);
730 					break;
731 			case XK_minus:
732 			case XK_KP_Subtract:
733 			case XK_Page_Down:
734 			case XK_KP_Page_Down:
735 					if (zoom_factor>5)	stk->ZoomView(win_size_x/2,win_size_y/2,-2*mult);
736 					stk->ExposeRegion(0,0,win_size_x,win_size_y);
737 					break;
738 			case XK_Right:
739 			case XK_KP_Right:
740 					stk->PanView( 10*mult, 0 );
741 					stk->ExposeRegion(0,0,win_size_x,win_size_y);
742 					break;
743 			case XK_Down:
744 			case XK_KP_Down:
745 					stk->PanView( 0, 10*mult );
746 					stk->ExposeRegion(0,0,win_size_x,win_size_y);
747 					break;
748 			case XK_Left:
749 			case XK_KP_Left:
750 					stk->PanView( -10*mult, 0 );
751 					stk->ExposeRegion(0,0,win_size_x,win_size_y);
752 					break;
753 			case XK_Up:
754 			case XK_KP_Up:
755 					stk->PanView( 0, -10*mult );
756 					stk->ExposeRegion(0,0,win_size_x,win_size_y);
757 					break;
758 			case XK_End:
759 			case XK_KP_End: {
760 					int x1,y1,x2,y2;
761 					stk->GetExtent(&x1,&y1,&x2,&y2);
762 					stk->PanView( x1-win_size_x/2+(x2-x1)/2, y1-win_size_y/2+(y2-y1)/2 );
763 					int	zf1=(int)(win_size_x*zoom_factor/(x2-x1));
764 					int	zf2=(int)(win_size_y*zoom_factor/(y2-y1));
765 					if (zf2<zf1)	zf1=zf2;
766 					if (zf1>3) {
767 						stk->ZoomView(win_size_x/2,win_size_y/2,zf1-zoom_factor);
768 						stk->ExposeRegion(0,0,win_size_x,win_size_y);
769 					}
770 					break;
771 			}
772 			case XK_Home:
773 			case XK_KP_Home: {
774 					int x1,y1,x2,y2;
775 					stk->GetExtent(&x1,&y1,&x2,&y2);
776 					stk->PanView( x1-10, y1-win_size_y/2+(y2-y1)/2 );
777 					stk->ZoomView(10,win_size_y/2,20-zoom_factor);
778 					stk->ExposeRegion(0,0,win_size_x,win_size_y);
779 					break;
780 			}
781 			case XK_Escape:
782 			case XK_Q:
783 			case XK_q:
784 					quit=1;
785 					break;
786 			default:
787 					break;
788 			}
789 			XDefineCursor(dpy,win,normal_cursor);
790 			XSync(dpy,0);
791 			warp_lock_x=WARP_NO_LOCK;	// just as a way tp unlock
792 			warp_lock_y=WARP_NO_LOCK;
793 			break;
794 		}
795 
796 		case ButtonPress:
797 			GetCurrentTime(1);	// Reset idle start
798 			stk->DispatchPress( &event.xbutton );
799 			break;
800 		case ButtonRelease:
801 			stk->DispatchRelease( &event.xbutton );
802 			break;
803 		case MotionNotify:
804 			if (warp_lock_x!=WARP_NO_LOCK) {
805 				do {
806 					if ( (!shapes&&warp_lock_x==event.xmotion.x&&warp_lock_y==event.xmotion.y)
807 						||((shapes)&&warp_lock_x==event.xmotion.x_root&&warp_lock_y==event.xmotion.y_root) ) {
808 						warp_lock_x=WARP_NO_LOCK;
809 						warp_lock_y=WARP_NO_LOCK;
810 						break;
811 					}
812 					printf( "#### motion event skipped due to warp lock.\n" );
813 				}
814 				while( XCheckMaskEvent(dpy,PointerMotionMask,&event) );
815 			}
816 			else {
817 				while( XCheckMaskEvent(dpy,PointerMotionMask,&event) );
818 				if (event.xmotion.state)	stk->DispatchMotion( &event.xmotion );
819 			}
820 			break;
821 
822 		case EnterNotify:
823 			XSetInputFocus( dpy, event.xcrossing.window, (int)None, CurrentTime );
824 			break;
825 
826 		case Expose:
827 			if (shapes) {
828 				stk->ExposeWindowRegion( event.xexpose.window, event.xexpose.x,
829 					event.xexpose.y, event.xexpose.width, event.xexpose.height );
830 			}
831 			else {
832 				stk->ExposeRegion(event.xexpose.x, event.xexpose.y,
833 						event.xexpose.width, event.xexpose.height );
834 			}
835 				if (rotate) {
836 					XSync(dpy,0);
837 					double	t1=GetCurrentTime();
838 					p->Rotation();
839 					double	t2=GetCurrentTime();
840 					printf( "Rotation Time: %.4g secs.\n", t2 - t1 );
841 					rotate=0;
842 				}
843 			break;
844 
845 		case ConfigureNotify:
846 			if (win_size_x!=event.xconfigure.width||win_size_y!=event.xconfigure.height) {
847 				win_size_x=event.xconfigure.width;
848 				win_size_y=event.xconfigure.height;
849 			}
850 			break;
851 		}
852 	}
853 
854 	delete img_buf;
855 
856 	printf( "terminated\n" );
857 	return 0;
858 }
859