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( ¤t );
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