neko: use WMHints stuff from Vincent.
[free-sw/xcb/demo] / neko / xcbneko.c
1 /*--------------------------------------------------------------
2  *
3  *      xneko
4  *
5  *                      Original Writer:
6  *            Masayuki Koba
7  *
8  *                      Programmed by:
9  *            Masayuki Koba, 1990
10  *
11  *          Added Color, RootWindow Capability and Quit Ability:
12  *            Dan Checkoway, 7-12-94
13  *
14  *                      Converted to use ANSI C and XCB by:
15  *            Ian Osgood, 2006
16  *
17  *--------------------------------------------------------------*/
18
19 #if USING_XLIB
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22 #include <X11/keysym.h>
23 #else
24 #define X_H   /* make sure we aren't using symbols from X.h */
25 #include <X11/XCB/xcb.h>
26 /*#include <X11/XCB/xcb_image.h>*/
27 #include <X11/XCB/xcb_aux.h>            /* XCBAuxGetScreen */
28 #include <X11/XCB/xcb_icccm.h>
29 #include <X11/XCB/xcb_atom.h>           /* STRING atom */
30 #include <X11/XCB/xcb_keysyms.h>
31
32 typedef enum { False, True } Bool;
33
34 #endif
35
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdio.h>
39
40 #include <unistd.h>     /* pause() */
41 #include <signal.h>
42 #include <math.h>
43 #include <sys/time.h>
44
45 #include "bitmaps/icon.xbm"
46 #include "bitmaps/cursor.xbm"
47 #include "bitmaps/cursor_mask.xbm"
48
49 #include "bitmaps/space.xbm"
50
51 #include "bitmaps/mati2.xbm"
52 #include "bitmaps/jare2.xbm"
53 #include "bitmaps/kaki1.xbm"
54 #include "bitmaps/kaki2.xbm"
55 #include "bitmaps/mati3.xbm"
56 #include "bitmaps/sleep1.xbm"
57 #include "bitmaps/sleep2.xbm"
58
59 #include "bitmaps/awake.xbm"
60
61 #include "bitmaps/up1.xbm"
62 #include "bitmaps/up2.xbm"
63 #include "bitmaps/down1.xbm"
64 #include "bitmaps/down2.xbm"
65 #include "bitmaps/left1.xbm"
66 #include "bitmaps/left2.xbm"
67 #include "bitmaps/right1.xbm"
68 #include "bitmaps/right2.xbm"
69 #include "bitmaps/upright1.xbm"
70 #include "bitmaps/upright2.xbm"
71 #include "bitmaps/upleft1.xbm"
72 #include "bitmaps/upleft2.xbm"
73 #include "bitmaps/dwleft1.xbm"
74 #include "bitmaps/dwleft2.xbm"
75 #include "bitmaps/dwright1.xbm"
76 #include "bitmaps/dwright2.xbm"
77
78 #include "bitmaps/utogi1.xbm"
79 #include "bitmaps/utogi2.xbm"
80 #include "bitmaps/dtogi1.xbm"
81 #include "bitmaps/dtogi2.xbm"
82 #include "bitmaps/ltogi1.xbm"
83 #include "bitmaps/ltogi2.xbm"
84 #include "bitmaps/rtogi1.xbm"
85 #include "bitmaps/rtogi2.xbm"
86
87 #define BITMAP_WIDTH            32
88 #define BITMAP_HEIGHT           32
89 #define WINDOW_WIDTH            320
90 #define WINDOW_HEIGHT           256
91 #define DEFAULT_BORDER          2
92 #define DEFAULT_WIN_X           1
93 #define DEFAULT_WIN_Y           1
94 #define AVAIL_KEYBUF            255
95
96 #define EVENT_MASK       ( XCBEventMaskKeyPress | XCBEventMaskButtonPress | \
97                                                    XCBEventMaskExposure | XCBEventMaskStructureNotify )
98
99 #define EVENT_MASK_ROOT  ( XCBEventMaskKeyPress | XCBEventMaskExposure )
100
101 #define MAX_TICK                9999            /* Odd Only! */
102 #define INTERVAL                125000L
103 #define NEKO_SPEED              16
104 #define IDLE_SPACE              6
105 #define NORMAL_STATE    1
106 #define DEBUG_LIST              2
107 #define DEBUG_MOVE              3
108
109 #define NEKO_STOP               0
110 #define NEKO_JARE               1
111 #define NEKO_KAKI               2
112 #define NEKO_AKUBI              3
113 #define NEKO_SLEEP              4
114 #define NEKO_AWAKE              5
115 #define NEKO_U_MOVE             6
116 #define NEKO_D_MOVE             7
117 #define NEKO_L_MOVE             8
118 #define NEKO_R_MOVE             9
119 #define NEKO_UL_MOVE    10
120 #define NEKO_UR_MOVE    11
121 #define NEKO_DL_MOVE    12
122 #define NEKO_DR_MOVE    13
123 #define NEKO_U_TOGI             14
124 #define NEKO_D_TOGI             15
125 #define NEKO_L_TOGI             16
126 #define NEKO_R_TOGI             17
127
128 #define NEKO_STOP_TIME          4
129 #define NEKO_JARE_TIME          10
130 #define NEKO_KAKI_TIME          4
131 #define NEKO_AKUBI_TIME         3
132 #define NEKO_AWAKE_TIME         3
133 #define NEKO_TOGI_TIME          10
134
135 #define PI_PER8                 ((double)3.1415926535/(double)8)
136
137 #define DIRNAMELEN              255
138
139 static int  useRoot;
140 char        *fgColor, *bgColor;
141
142 static char  *ProgramName;
143
144 XCBConnection   *xc;
145 XCBSCREEN      *theScreen;              /* instead of macro(theDisplay, int theScreen) */
146 unsigned long  theFgPixel;
147 unsigned long  theBgPixel;
148 XCBWINDOW         theWindow;
149 XCBCURSOR         theCursor;
150 XCBKeySymbols   *theKeySyms;
151 XCBATOM deleteWindowAtom;
152
153 static unsigned int  WindowWidth;
154 static unsigned int  WindowHeight;
155 static int           WindowPointX;
156 static int           WindowPointY;
157
158 static unsigned int     BorderWidth = DEFAULT_BORDER;
159
160 long    IntervalTime = INTERVAL;
161
162 int             EventState;
163
164 int             NekoTickCount;
165 int             NekoStateCount;
166 int             NekoState;
167
168 int             MouseX;
169 int             MouseY;
170
171 int             PrevMouseX = 0;
172 int             PrevMouseY = 0;
173
174 int             NekoX;
175 int             NekoY;
176
177 int             NekoMoveDx;
178 int             NekoMoveDy;
179
180 int             NekoLastX;
181 int             NekoLastY;
182 XCBGCONTEXT             NekoLastGC;
183
184 double  NekoSpeed = (double)NEKO_SPEED;
185
186 double  SinPiPer8Times3;
187 double  SinPiPer8;
188
189 XCBPIXMAP       SpaceXbm;
190
191 XCBPIXMAP       Mati2Xbm;
192 XCBPIXMAP       Jare2Xbm;
193 XCBPIXMAP       Kaki1Xbm;
194 XCBPIXMAP       Kaki2Xbm;
195 XCBPIXMAP       Mati3Xbm;
196 XCBPIXMAP       Sleep1Xbm;
197 XCBPIXMAP       Sleep2Xbm;
198
199 XCBPIXMAP       AwakeXbm;
200
201 XCBPIXMAP       Up1Xbm;
202 XCBPIXMAP       Up2Xbm;
203 XCBPIXMAP       Down1Xbm;
204 XCBPIXMAP       Down2Xbm;
205 XCBPIXMAP       Left1Xbm;
206 XCBPIXMAP       Left2Xbm;
207 XCBPIXMAP       Right1Xbm;
208 XCBPIXMAP       Right2Xbm;
209 XCBPIXMAP       UpLeft1Xbm;
210 XCBPIXMAP       UpLeft2Xbm;
211 XCBPIXMAP       UpRight1Xbm;
212 XCBPIXMAP       UpRight2Xbm;
213 XCBPIXMAP       DownLeft1Xbm;
214 XCBPIXMAP       DownLeft2Xbm;
215 XCBPIXMAP       DownRight1Xbm;
216 XCBPIXMAP       DownRight2Xbm;
217
218 XCBPIXMAP       UpTogi1Xbm;
219 XCBPIXMAP       UpTogi2Xbm;
220 XCBPIXMAP       DownTogi1Xbm;
221 XCBPIXMAP       DownTogi2Xbm;
222 XCBPIXMAP       LeftTogi1Xbm;
223 XCBPIXMAP       LeftTogi2Xbm;
224 XCBPIXMAP       RightTogi1Xbm;
225 XCBPIXMAP       RightTogi2Xbm;
226
227 XCBGCONTEXT     SpaceGC;
228
229 XCBGCONTEXT     Mati2GC;
230 XCBGCONTEXT     Jare2GC;
231 XCBGCONTEXT     Kaki1GC;
232 XCBGCONTEXT     Kaki2GC;
233 XCBGCONTEXT     Mati3GC;
234 XCBGCONTEXT     Sleep1GC;
235 XCBGCONTEXT     Sleep2GC;
236
237 XCBGCONTEXT     AwakeGC;
238
239 XCBGCONTEXT     Up1GC;
240 XCBGCONTEXT     Up2GC;
241 XCBGCONTEXT     Down1GC;
242 XCBGCONTEXT     Down2GC;
243 XCBGCONTEXT     Left1GC;
244 XCBGCONTEXT     Left2GC;
245 XCBGCONTEXT     Right1GC;
246 XCBGCONTEXT     Right2GC;
247 XCBGCONTEXT     UpLeft1GC;
248 XCBGCONTEXT     UpLeft2GC;
249 XCBGCONTEXT     UpRight1GC;
250 XCBGCONTEXT     UpRight2GC;
251 XCBGCONTEXT     DownLeft1GC;
252 XCBGCONTEXT     DownLeft2GC;
253 XCBGCONTEXT     DownRight1GC;
254 XCBGCONTEXT     DownRight2GC;
255
256 XCBGCONTEXT     UpTogi1GC;
257 XCBGCONTEXT     UpTogi2GC;
258 XCBGCONTEXT     DownTogi1GC;
259 XCBGCONTEXT     DownTogi2GC;
260 XCBGCONTEXT     LeftTogi1GC;
261 XCBGCONTEXT     LeftTogi2GC;
262 XCBGCONTEXT     RightTogi1GC;
263 XCBGCONTEXT     RightTogi2GC;
264
265 typedef struct {
266   XCBGCONTEXT            *GCCreatePtr;
267   XCBPIXMAP        *BitmapCreatePtr;
268   char          *PixelPattern;
269   unsigned int  PixelWidth;
270   unsigned int  PixelHeight;
271 } BitmapGCData;
272
273 BitmapGCData    BitmapGCDataTable[] = {
274   { &SpaceGC, &SpaceXbm, space_bits, space_width, space_height },
275   { &Mati2GC, &Mati2Xbm, mati2_bits, mati2_width, mati2_height },
276   { &Jare2GC, &Jare2Xbm, jare2_bits, jare2_width, jare2_height },
277   { &Kaki1GC, &Kaki1Xbm, kaki1_bits, kaki1_width, kaki1_height },
278   { &Kaki2GC, &Kaki2Xbm, kaki2_bits, kaki2_width, kaki2_height },
279   { &Mati3GC, &Mati3Xbm, mati3_bits, mati3_width, mati3_height },
280   { &Sleep1GC, &Sleep1Xbm, sleep1_bits, sleep1_width, sleep1_height },
281   { &Sleep2GC, &Sleep2Xbm, sleep2_bits, sleep2_width, sleep2_height },
282   { &AwakeGC, &AwakeXbm, awake_bits, awake_width, awake_height },
283   { &Up1GC, &Up1Xbm, up1_bits, up1_width, up1_height },
284   { &Up2GC, &Up2Xbm, up2_bits, up2_width, up2_height },
285   { &Down1GC, &Down1Xbm, down1_bits, down1_width, down1_height },
286   { &Down2GC, &Down2Xbm, down2_bits, down2_width, down2_height },
287   { &Left1GC, &Left1Xbm, left1_bits, left1_width, left1_height },
288   { &Left2GC, &Left2Xbm, left2_bits, left2_width, left2_height },
289   { &Right1GC, &Right1Xbm, right1_bits, right1_width, right1_height },
290   { &Right2GC, &Right2Xbm, right2_bits, right2_width, right2_height },
291   { &UpLeft1GC, &UpLeft1Xbm, upleft1_bits, upleft1_width, upleft1_height },
292   { &UpLeft2GC, &UpLeft2Xbm, upleft2_bits, upleft2_width, upleft2_height },
293   { &UpRight1GC,
294           &UpRight1Xbm, upright1_bits, upright1_width, upright1_height },
295   { &UpRight2GC,
296       &UpRight2Xbm, upright2_bits, upright2_width, upright2_height },
297   { &DownLeft1GC, &DownLeft1Xbm, dwleft1_bits, dwleft1_width, dwleft1_height },
298   { &DownLeft2GC, &DownLeft2Xbm, dwleft2_bits, dwleft2_width, dwleft2_height },
299   { &DownRight1GC,
300           &DownRight1Xbm, dwright1_bits, dwright1_width, dwright1_height },
301   { &DownRight2GC,
302       &DownRight2Xbm, dwright2_bits, dwright2_width, dwright2_height },
303   { &UpTogi1GC, &UpTogi1Xbm, utogi1_bits, utogi1_width, utogi1_height },
304   { &UpTogi2GC, &UpTogi2Xbm, utogi2_bits, utogi2_width, utogi2_height },
305   { &DownTogi1GC, &DownTogi1Xbm, dtogi1_bits, dtogi1_width, dtogi1_height },
306   { &DownTogi2GC, &DownTogi2Xbm, dtogi2_bits, dtogi2_width, dtogi2_height },
307   { &LeftTogi1GC, &LeftTogi1Xbm, ltogi1_bits, ltogi1_width, ltogi1_height },
308   { &LeftTogi2GC, &LeftTogi2Xbm, ltogi2_bits, ltogi2_width, ltogi2_height },
309   { &RightTogi1GC,
310       &RightTogi1Xbm, rtogi1_bits, rtogi1_width, rtogi1_height },
311   { &RightTogi2GC,
312       &RightTogi2Xbm, rtogi2_bits, rtogi2_width, rtogi2_height },
313   { NULL, NULL, NULL, 0, 0 }
314 };
315
316 typedef struct {
317   XCBGCONTEXT  *TickEvenGCPtr;
318   XCBGCONTEXT  *TickOddGCPtr;
319 } Animation;
320
321 Animation  AnimationPattern[] = {
322   { &Mati2GC, &Mati2GC },               /* NekoState == NEKO_STOP */
323   { &Jare2GC, &Mati2GC },               /* NekoState == NEKO_JARE */
324   { &Kaki1GC, &Kaki2GC },               /* NekoState == NEKO_KAKI */
325   { &Mati3GC, &Mati3GC },               /* NekoState == NEKO_AKUBI */
326   { &Sleep1GC, &Sleep2GC },             /* NekoState == NEKO_SLEEP */
327   { &AwakeGC, &AwakeGC },               /* NekoState == NEKO_AWAKE */
328   { &Up1GC, &Up2GC }    ,               /* NekoState == NEKO_U_MOVE */
329   { &Down1GC, &Down2GC },               /* NekoState == NEKO_D_MOVE */
330   { &Left1GC, &Left2GC },               /* NekoState == NEKO_L_MOVE */
331   { &Right1GC, &Right2GC },             /* NekoState == NEKO_R_MOVE */
332   { &UpLeft1GC, &UpLeft2GC },           /* NekoState == NEKO_UL_MOVE */
333   { &UpRight1GC, &UpRight2GC },     /* NekoState == NEKO_UR_MOVE */
334   { &DownLeft1GC, &DownLeft2GC },       /* NekoState == NEKO_DL_MOVE */
335   { &DownRight1GC, &DownRight2GC },     /* NekoState == NEKO_DR_MOVE */
336   { &UpTogi1GC, &UpTogi2GC },           /* NekoState == NEKO_U_TOGI */
337   { &DownTogi1GC, &DownTogi2GC },       /* NekoState == NEKO_D_TOGI */
338   { &LeftTogi1GC, &LeftTogi2GC },       /* NekoState == NEKO_L_TOGI */
339   { &RightTogi1GC, &RightTogi2GC },     /* NekoState == NEKO_R_TOGI */
340 };
341
342
343 /* PutImage.c: format/unit/order conversion should move out to a library */
344 static unsigned char const _reverse_byte[0x100] = {
345         0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
346         0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
347         0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
348         0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
349         0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
350         0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
351         0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
352         0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
353         0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
354         0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
355         0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
356         0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
357         0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
358         0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
359         0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
360         0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
361         0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
362         0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
363         0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
364         0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
365         0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
366         0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
367         0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
368         0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
369         0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
370         0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
371         0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
372         0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
373         0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
374         0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
375         0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
376         0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
377 };
378
379 /* convert 1Ll <--> 4Mm */
380 static void
381 SwapBits(
382     register unsigned char *src,
383     register unsigned char *dest,
384     long srclen, long srcinc, long destinc,
385     unsigned int height)
386 {
387     register long h, n;
388     register const unsigned char *rev = _reverse_byte;
389
390     srcinc -= srclen;
391     destinc -= srclen;
392     for (h = height; --h >= 0; src += srcinc, dest += destinc)
393         for (n = srclen; --n >= 0; )
394             *dest++ = rev[*src++];
395 }
396
397 /* assumes pad is a power of 2 */
398 #define ROUNDUP(nbytes, pad) (((nbytes) + ((pad) - 1)) & ~(long)((pad) - 1))
399
400 /* CrPFBData.c and CrBFData.c (very similar) */
401 /*  if depth==1, behaves like CreateBitmapFromData */
402 XCBPIXMAP CreatePixmapFromBitmapData( XCBConnection *c,
403         XCBWINDOW window, char *data, CARD16 w, CARD16 h,
404         CARD32 fg, CARD32 bg, CARD32 depth)
405 {
406   XCBDRAWABLE drawable;
407   XCBPIXMAP bitmap = XCBPIXMAPNew( c );
408
409   drawable.window = window;
410   XCBCreatePixmap( c, depth, bitmap, drawable, w, h );
411   
412   XCBGCONTEXT gc = XCBGCONTEXTNew( c );
413   
414   CARD32 mask = (depth==1 ? 0 : XCBGCForeground | XCBGCBackground);
415   CARD32 values[] = { fg, bg };
416
417   drawable.pixmap = bitmap;
418   XCBCreateGC( c, gc, drawable, mask, values );
419   
420   /* XImage attributes: bpp=1, xoffset=0,
421        byte_order=bit_order=LSB, unit=8, pad=8,   bpl=(w+7/8) */
422
423   /*  must swap and pad the data if bit/byte_order isn't LSB (Mac) */
424   
425   /* Mac X Server: byte_order=bit_order=MSB, unit=32, padding=32 */
426   long bpl = (w+7)/8;
427   long pad = XCBGetSetup(c)->bitmap_format_scanline_pad;
428   long bpd = ROUNDUP(w, pad)>>3;
429   long bufLen = bpd * h;
430   BYTE buf[1024];
431   if (XCBGetSetup(c)->bitmap_format_scanline_unit == 32 &&
432       XCBGetSetup(c)->bitmap_format_bit_order == XCBImageOrderMSBFirst &&
433       XCBGetSetup(c)->image_byte_order == XCBImageOrderMSBFirst)
434   {
435     SwapBits((unsigned char *)data, buf, bpl, bpl, bpd, h);
436   }
437   else if (bpl != bpd)
438   {
439     int i;
440     BYTE *src = (BYTE *)data, *dest = buf;
441     for (i=0; i<h; i++, dest += bpd, src += bpl)
442       memcpy(dest, src, bpl);
443   }
444   else
445     memcpy(buf, data, bufLen);
446
447   /* note: CBfD uses XYPixmap, but CPfBD uses XYBitmap
448            there shouldn't be a difference when depth==1,
449            but the neko images are corrupt if using XYPixmap */
450   CARD8 format = (depth==1 ? XCBImageFormatXYPixmap : XCBImageFormatXYBitmap);
451   
452   /* PutImage.c: left_pad = (image->xoffset + req->xoffset) & (dpy->bitmap_unit-1)
453        screen->bitmap_format_scanline_unit
454        left_pad = (0 + 0) & (32 - 1) = 0 */
455
456   XCBPutImage( c, format, drawable, gc,
457         w, h, 0, 0,
458         0, 1,           /* left_pad, depth */
459         bufLen, buf);
460
461 #if DEBUG
462   XCBGenericError *error = NULL;
463   XCBSync( c, &error );
464   if (error) {
465     printf("error code %d", (int)error->error_code);
466     free(error);
467   }
468 #endif
469
470   XCBFreeGC( c, gc );
471   
472   /* later: XCBFreePixmap( c, bitmap ); */
473   return bitmap;
474 }
475
476 XCBPIXMAP CreateBitmapFromData(XCBConnection *c, XCBWINDOW window,
477         char *data, CARD16 w, CARD16 h)
478 {
479         CARD32 depth = 1;
480         return CreatePixmapFromBitmapData(c, window, data, w, h, 0, 0, depth);
481 }
482
483 void  InitBitmapAndGCs(void) {
484   BitmapGCData  *BitmapGCDataTablePtr;
485   CARD32 theGCValues[5];
486   XCBDRAWABLE drawable;   drawable.window = theWindow;
487
488   theGCValues[0] = XCBGXcopy;
489
490   theGCValues[1] = theFgPixel;
491   theGCValues[2] = theBgPixel;
492
493   theGCValues[3] = XCBFillStyleTiled;
494   
495   /* TODO: latency: make all the bitmaps, then all the contexts? */
496
497   for ( BitmapGCDataTablePtr = BitmapGCDataTable;
498         BitmapGCDataTablePtr->GCCreatePtr != NULL;
499         BitmapGCDataTablePtr++ ) {
500
501         *(BitmapGCDataTablePtr->BitmapCreatePtr)
502           = CreatePixmapFromBitmapData( xc, theScreen->root,
503                 BitmapGCDataTablePtr->PixelPattern,
504                 BitmapGCDataTablePtr->PixelWidth,
505                 BitmapGCDataTablePtr->PixelHeight,
506                 theFgPixel, theBgPixel, theScreen->root_depth);
507
508         theGCValues[4] = BitmapGCDataTablePtr->BitmapCreatePtr->xid; /* tile */
509         
510         *(BitmapGCDataTablePtr->GCCreatePtr) = XCBGCONTEXTNew( xc );
511         XCBCreateGC( xc, *(BitmapGCDataTablePtr->GCCreatePtr), drawable,
512                   XCBGCFunction | XCBGCForeground | XCBGCBackground |
513                   XCBGCFillStyle | XCBGCTile,
514                   theGCValues );
515   }
516   
517   /* later: XCBFreePixmap( c, bitmap ); */
518   /* later: XCBFreeGC( c, gc ); */
519 }
520
521 XCBATOM
522 GetAtom(XCBConnection *c, const char *atomName)
523 {
524         XCBATOM atom = { XCBNone };
525         XCBInternAtomRep *r = XCBInternAtomReply(c,
526                 XCBInternAtom(c, 0, strlen(atomName), atomName), NULL);
527         if (r) {
528                 atom = r->atom;
529                 free(r);
530         }
531         return atom;
532 }
533
534 void
535 InitScreen( char *DisplayName, char *theGeometry, char *theTitle, Bool iconicState )
536 {
537   XCBPIXMAP             theCursorSource;
538   XCBPIXMAP             theCursorMask;
539   unsigned int   theDepth;
540   XCBCOLORMAP           theColormap;
541   int screen;
542   
543   if ( ( xc = XCBConnect( DisplayName, &screen ) ) == NULL ) {
544         fprintf( stderr, "%s: Can't open connection", ProgramName );
545         if ( DisplayName != NULL )
546           fprintf( stderr, " %s.\n", DisplayName );
547         else
548           fprintf( stderr, ".\n" );
549         exit( 1 );
550   }
551   
552   theScreen = XCBAuxGetScreen(xc, screen);
553   if (theScreen == NULL) {
554         fprintf( stderr, "%s: Can't get default screen", ProgramName );
555         exit( 1 );
556   }
557   
558   theDepth    = theScreen->root_depth;  /* DefaultDepth */
559   theColormap = theScreen->default_colormap;
560
561   WindowPointX = DEFAULT_WIN_X;
562   WindowPointY = DEFAULT_WIN_Y;
563   WindowWidth  = WINDOW_WIDTH;
564   WindowHeight = WINDOW_HEIGHT;
565
566 #ifdef TODO  
567   int                   GeometryStatus;
568   GeometryStatus = XParseGeometry( theGeometry,
569                                                                   &WindowPointX, &WindowPointY,
570                                                                   &WindowWidth, &WindowHeight );
571 #endif
572   
573   theCursorSource = CreateBitmapFromData( xc,
574                                                                                   theScreen->root,
575                                                                                   cursor_bits,
576                                                                                   cursor_width,
577                                                                                   cursor_height );
578   
579   theCursorMask = CreateBitmapFromData( xc,
580                                                                                 theScreen->root,
581                                                                                 cursor_mask_bits,
582                                                                                 cursor_mask_width,
583                                                                                 cursor_mask_height );
584
585
586   if ( bgColor == NULL) bgColor = "white";
587   if ( fgColor == NULL) fgColor = "black";
588   
589   XCBAllocNamedColorCookie bgCookie = XCBAllocNamedColor ( xc,
590                 theColormap,  strlen(bgColor), bgColor );
591
592   XCBAllocNamedColorCookie fgCookie = XCBAllocNamedColor ( xc,
593                 theColormap,  strlen(fgColor), fgColor );
594
595   /* mouse cursor is always black and white */
596   XCBAllocNamedColorCookie blackCookie = XCBAllocNamedColor ( xc,
597                 theColormap,  strlen("black"), "black" );
598   XCBAllocNamedColorCookie whiteCookie = XCBAllocNamedColor ( xc,
599                 theColormap,  strlen("white"), "white" );
600                 
601   XCBAllocNamedColorRep *bgRep = XCBAllocNamedColorReply( xc, bgCookie, 0 );
602   if (!bgRep) {
603         fprintf( stderr,
604                         "%s: Can't allocate the background color %s.\n", ProgramName, bgColor );
605         exit( 1 );
606   }
607   theBgPixel = bgRep->pixel;
608
609   XCBAllocNamedColorRep *fgRep = XCBAllocNamedColorReply( xc, fgCookie, 0 );
610   if (!fgRep) {
611         fprintf( stderr,
612                         "%s: Can't allocate the foreground color %s.\n", ProgramName, fgColor );
613         exit( 1 );
614   }
615   theFgPixel = fgRep->pixel;
616
617   XCBAllocNamedColorRep *blackRep = XCBAllocNamedColorReply( xc, blackCookie, 0 );
618   if (!blackRep) {
619         fprintf( stderr,
620                         "%s: Can't allocate the black color.\n", ProgramName );
621         exit( 1 );
622   }
623   XCBAllocNamedColorRep *whiteRep = XCBAllocNamedColorReply( xc, whiteCookie, 0 );
624   if (!whiteRep) {
625         fprintf( stderr,
626                         "%s: Can't allocate the white color.\n", ProgramName );
627         exit( 1 );
628   }
629   
630   theCursor = XCBCURSORNew( xc );
631   XCBCreateCursor ( xc, theCursor, theCursorSource, theCursorMask,
632         blackRep->visual_red, blackRep->visual_green, blackRep->visual_blue,
633         whiteRep->visual_red, whiteRep->visual_green, whiteRep->visual_blue,
634         cursor_x_hot, cursor_y_hot );
635
636   free(bgRep);
637   free(fgRep);
638   free(blackRep);
639   free(whiteRep);
640
641   if ( useRoot ) {
642     CARD32 rootAttributes[] = { theBgPixel, EVENT_MASK_ROOT, theCursor.xid };
643         theWindow = theScreen->root;
644         XCBChangeWindowAttributes(xc, theWindow,
645                 XCBCWBackPixel | XCBCWEventMask | XCBCWCursor, rootAttributes );
646         
647         /* XClearWindow: clear area with all dimensions 0 */
648         XCBClearArea( xc, False, theWindow, 0, 0, 0, 0 );
649         
650         XCBDRAWABLE d = { theWindow };
651         XCBGetGeometryRep *geometry = XCBGetGeometryReply( xc,
652           XCBGetGeometry( xc, d ), NULL);
653         if (geometry) {
654           /* only width & height are used by the program */
655           WindowWidth  = geometry->width;
656           WindowHeight = geometry->height;
657           free(geometry);
658         }
659         
660         /* TODO: grab key Alt-Q to quit gracefully? */
661   }
662   else {
663         XCBPIXMAP                theIconPixmap;
664
665         CARD32 theWindowAttributes[] = {
666                 theBgPixel,    /* background */
667                 theFgPixel,    /* border */
668                 False,         /* override_redirect */
669                 EVENT_MASK,
670                 theCursor.xid };
671
672         unsigned long theWindowMask = XCBCWBackPixel | XCBCWBorderPixel |
673           XCBCWOverrideRedirect | XCBCWEventMask | XCBCWCursor;
674         
675         theWindow = XCBWINDOWNew( xc );
676         XCBCreateWindow( xc,
677                 theDepth,
678                 theWindow,
679                 theScreen->root,
680                 WindowPointX, WindowPointY,
681                 WindowWidth, WindowHeight,
682                 BorderWidth,
683                 XCBWindowClassInputOutput,
684                 theScreen->root_visual, /* CopyFromParent */
685                 theWindowMask, theWindowAttributes );
686
687         /* new: obey the window-delete protocol, look for XCBClientMessage */
688         deleteWindowAtom = GetAtom(xc, "WM_DELETE_WINDOW");
689         SetWMProtocols( xc, theWindow, 1, &deleteWindowAtom );
690
691         theIconPixmap = CreateBitmapFromData( xc, theWindow,
692                                                                                   icon_bits, icon_width, icon_height );
693
694         WMHints *theWMHints = AllocWMHints();
695
696         WMHintsSetIconPixmap( theWMHints, theIconPixmap );
697
698         if ( iconicState )
699           WMHintsSetIconic( theWMHints );
700         else
701           WMHintsSetNormal( theWMHints );
702         
703         SetWMHints( xc, theWindow, theWMHints);
704         
705         free( theWMHints );
706
707         /* why hide the structure? */
708         SizeHints *theSizeHints = AllocSizeHints();
709
710         /* need enum for second param (user specified) */
711         SizeHintsSetPosition(theSizeHints, 0, WindowPointX, WindowPointY);
712         SizeHintsSetSize(theSizeHints, 0, WindowWidth, WindowHeight);
713
714         SetWMNormalHints(xc, theWindow, theSizeHints);
715
716         FreeSizeHints(theSizeHints);
717
718         /* Um, why do I have to specify the encoding in this API? */
719         SetWMName( xc, theWindow, STRING, strlen(theTitle), theTitle );
720         SetWMIconName( xc, theWindow, STRING, strlen(theTitle), theTitle );
721
722         XCBMapWindow( xc, theWindow );
723
724   }
725   
726   InitBitmapAndGCs();
727
728   XCBFlush(xc);
729
730   /* latency: ask for keysyms now, and receive them later */
731   theKeySyms = XCBKeySymbolsAlloc( xc );
732
733   /* later: XCBKeySymbolsFree( keysyms ); */
734   /* later: XCBRefreshKeyboardMapping ( keysyms, mappingEvent ); */
735 }
736
737
738 void  Interval(void) {
739   pause();
740 }
741
742
743 void  TickCount(void) {
744   if ( ++NekoTickCount >= MAX_TICK )
745         NekoTickCount = 0;
746   
747   if ( NekoTickCount % 2 == 0 )
748         if ( NekoStateCount < MAX_TICK )
749           NekoStateCount++;
750 }
751
752
753 void
754 SetNekoState( int SetValue )
755 {
756   NekoTickCount = 0;
757   NekoStateCount = 0;
758   
759   NekoState = SetValue;
760   
761 #ifdef  DEBUG
762   switch ( NekoState ) {
763   case NEKO_STOP:
764   case NEKO_JARE:
765   case NEKO_KAKI:
766   case NEKO_AKUBI:
767   case NEKO_SLEEP:
768   case NEKO_U_TOGI:
769   case NEKO_D_TOGI:
770   case NEKO_L_TOGI:
771   case NEKO_R_TOGI:
772         NekoMoveDx = NekoMoveDy = 0;
773         break;
774   default:
775         break;
776   }
777 #endif
778 }
779
780 /* FillRct.c */
781 /*   Xlib does merging of requests, but the Flush and frequent DrawGC changes
782      defeat this mechanism */
783
784 void
785 DrawNeko( int x, int y, XCBGCONTEXT DrawGC )
786 {
787   XCBDRAWABLE drawable;  drawable.window = theWindow;
788   XCBRECTANGLE rect = { NekoLastX, NekoLastY, BITMAP_WIDTH, BITMAP_HEIGHT };
789
790   if ( (x != NekoLastX || y != NekoLastY) && (EventState != DEBUG_LIST) )
791   {
792         XCBPolyFillRectangle( xc, drawable, SpaceGC, 1, &rect );
793         rect.x = x; rect.y = y;
794
795   }
796
797   CARD32 originMask = XCBGCTileStippleOriginX | XCBGCTileStippleOriginY;
798   CARD32 origin[2] = { x, y };
799   XCBChangeGC( xc, DrawGC, originMask, origin );
800   /* XSetTSOrigin( theDisplay, DrawGC, x, y ); */
801
802   XCBPolyFillRectangle( xc, drawable, DrawGC, 1, &rect );
803
804   XCBFlush( xc );
805
806   NekoLastX = x;
807   NekoLastY = y;
808   
809   NekoLastGC = DrawGC;
810 }
811
812
813 void  RedrawNeko(void) {
814   XCBDRAWABLE drawable;  drawable.window = theWindow;
815   XCBRECTANGLE rect = { NekoLastX, NekoLastY, BITMAP_WIDTH, BITMAP_HEIGHT };
816
817   XCBPolyFillRectangle( xc, drawable, NekoLastGC, 1, &rect );
818
819   XCBFlush( xc );
820 }
821
822
823
824 void  NekoDirection(void) {
825   int                   NewState;
826   double                LargeX, LargeY;
827   double                Length;
828   double                SinTheta;
829   
830   if ( NekoMoveDx == 0 && NekoMoveDy == 0 ) {
831         NewState = NEKO_STOP;
832   } else {
833         LargeX = (double)NekoMoveDx;
834         LargeY = (double)(-NekoMoveDy);
835         Length = sqrt( LargeX * LargeX + LargeY * LargeY );
836         SinTheta = LargeY / Length;
837         
838         if ( NekoMoveDx > 0 ) {
839           if ( SinTheta > SinPiPer8Times3 ) {
840                 NewState = NEKO_U_MOVE;
841           } else if ( ( SinTheta <= SinPiPer8Times3 )
842                                  && ( SinTheta > SinPiPer8 ) ) {
843                 NewState = NEKO_UR_MOVE;
844           } else if ( ( SinTheta <= SinPiPer8 )
845                                  && ( SinTheta > -( SinPiPer8 ) ) ) {
846                 NewState = NEKO_R_MOVE;
847           } else if ( ( SinTheta <= -( SinPiPer8 ) )
848                                  && ( SinTheta > -( SinPiPer8Times3 ) ) ) {
849                 NewState = NEKO_DR_MOVE;
850           } else {
851                 NewState = NEKO_D_MOVE;
852           }
853         } else {
854           if ( SinTheta > SinPiPer8Times3 ) {
855                 NewState = NEKO_U_MOVE;
856           } else if ( ( SinTheta <= SinPiPer8Times3 )
857                                  && ( SinTheta > SinPiPer8 ) ) {
858                 NewState = NEKO_UL_MOVE;
859           } else if ( ( SinTheta <= SinPiPer8 )
860                                  && ( SinTheta > -( SinPiPer8 ) ) ) {
861                 NewState = NEKO_L_MOVE;
862           } else if ( ( SinTheta <= -( SinPiPer8 ) )
863                                  && ( SinTheta > -( SinPiPer8Times3 ) ) ) {
864                 NewState = NEKO_DL_MOVE;
865           } else {
866                 NewState = NEKO_D_MOVE;
867           }
868         }
869   }
870   
871   if ( NekoState != NewState ) {
872         SetNekoState( NewState );
873   }
874 }
875
876
877 Bool  IsWindowOver(void) {
878   Bool  ReturnValue = False;
879   
880   if ( NekoY <= 0 ) {
881         NekoY = 0;
882         ReturnValue = True;
883   } else if ( NekoY >= WindowHeight - BITMAP_HEIGHT ) {
884         NekoY = WindowHeight - BITMAP_HEIGHT;
885         ReturnValue = True;
886   }
887   if ( NekoX <= 0 ) {
888         NekoX = 0;
889         ReturnValue = True;
890   } else if ( NekoX >= WindowWidth - BITMAP_WIDTH ) {
891         NekoX = WindowWidth - BITMAP_WIDTH;
892         ReturnValue = True;
893   }
894   
895   return( ReturnValue );
896 }
897
898
899 Bool  IsNekoDontMove(void) {
900   return( NekoX == NekoLastX  &&  NekoY == NekoLastY );
901 }
902
903
904 Bool  IsNekoMoveStart(void) {
905 #ifndef DEBUG
906   if ( (PrevMouseX >= MouseX - IDLE_SPACE
907                 && PrevMouseX <= MouseX + IDLE_SPACE) &&
908           (PrevMouseY >= MouseY - IDLE_SPACE 
909            && PrevMouseY <= MouseY + IDLE_SPACE) )
910         return( False );
911   else
912         return( True );
913 #else
914   if ( NekoMoveDx == 0 && NekoMoveDy == 0 )
915         return( False );
916   else
917         return( True );
918 #endif
919 }
920
921 #ifndef DEBUG
922 void  CalcDxDy(void) {
923   int                   RelativeX, RelativeY;
924   double                LargeX, LargeY;
925   double                DoubleLength, Length;
926   
927   /* TODO: replace query with pointer motion notification? */
928
929   XCBQueryPointerRep *reply = XCBQueryPointerReply( xc,
930         XCBQueryPointer( xc, theWindow ), NULL);
931         
932   RelativeX = reply->win_x;
933   RelativeY = reply->win_y;
934   
935   free(reply);
936
937   PrevMouseX = MouseX;
938   PrevMouseY = MouseY;
939   
940   MouseX = RelativeX;
941   MouseY = RelativeY;
942   
943   LargeX = (double)( MouseX - NekoX - BITMAP_WIDTH / 2 );
944   LargeY = (double)( MouseY - NekoY - BITMAP_HEIGHT );
945   
946   DoubleLength = LargeX * LargeX + LargeY * LargeY;
947   
948   if ( DoubleLength != (double)0 ) {
949         Length = sqrt( DoubleLength );
950         if ( Length <= NekoSpeed ) {
951           NekoMoveDx = (int)LargeX;
952           NekoMoveDy = (int)LargeY;
953         } else {
954           NekoMoveDx = (int)( ( NekoSpeed * LargeX ) / Length );
955           NekoMoveDy = (int)( ( NekoSpeed * LargeY ) / Length );
956         }
957   } else {
958         NekoMoveDx = NekoMoveDy = 0;
959   }
960 }
961 #endif
962
963 void  NekoThinkDraw(void) {
964 #ifndef DEBUG
965   CalcDxDy();
966 #endif
967
968   if ( NekoState != NEKO_SLEEP ) {
969         DrawNeko( NekoX, NekoY,
970                          NekoTickCount % 2 == 0 ?
971                          *(AnimationPattern[ NekoState ].TickEvenGCPtr) :
972                          *(AnimationPattern[ NekoState ].TickOddGCPtr) );
973   } else {
974         DrawNeko( NekoX, NekoY,
975                          NekoTickCount % 8 <= 3 ?
976                          *(AnimationPattern[ NekoState ].TickEvenGCPtr) :
977                          *(AnimationPattern[ NekoState ].TickOddGCPtr) );
978   }
979   
980   TickCount();
981   
982   switch ( NekoState ) {
983   case NEKO_STOP:
984         if ( IsNekoMoveStart() ) {
985           SetNekoState( NEKO_AWAKE );
986           break;
987         }
988         if ( NekoStateCount < NEKO_STOP_TIME ) {
989           break;
990         }
991         if ( NekoMoveDx < 0 && NekoX <= 0 ) {
992           SetNekoState( NEKO_L_TOGI );
993         } else if ( NekoMoveDx > 0 && NekoX >= WindowWidth - BITMAP_WIDTH ) {
994           SetNekoState( NEKO_R_TOGI );
995         } else if ( NekoMoveDy < 0 && NekoY <= 0 ) {
996           SetNekoState( NEKO_U_TOGI );
997         } else if ( NekoMoveDy > 0 && NekoY >= WindowHeight - BITMAP_HEIGHT ) {
998           SetNekoState( NEKO_D_TOGI );
999         } else {
1000           SetNekoState( NEKO_JARE );
1001         }
1002         break;
1003   case NEKO_JARE:
1004         if ( IsNekoMoveStart() ) {
1005           SetNekoState( NEKO_AWAKE );
1006           break;
1007         }
1008         if ( NekoStateCount < NEKO_JARE_TIME ) {
1009           break;
1010         }
1011         SetNekoState( NEKO_KAKI );
1012         break;
1013   case NEKO_KAKI:
1014         if ( IsNekoMoveStart() ) {
1015           SetNekoState( NEKO_AWAKE );
1016           break;
1017         }
1018         if ( NekoStateCount < NEKO_KAKI_TIME ) {
1019           break;
1020         }
1021         SetNekoState( NEKO_AKUBI );
1022         break;
1023   case NEKO_AKUBI:
1024         if ( IsNekoMoveStart() ) {
1025           SetNekoState( NEKO_AWAKE );
1026           break;
1027         }
1028         if ( NekoStateCount < NEKO_AKUBI_TIME ) {
1029           break;
1030         }
1031         SetNekoState( NEKO_SLEEP );
1032         break;
1033   case NEKO_SLEEP:
1034         if ( IsNekoMoveStart() ) {
1035           SetNekoState( NEKO_AWAKE );
1036           break;
1037         }
1038         break;
1039   case NEKO_AWAKE:
1040         if ( NekoStateCount < NEKO_AWAKE_TIME ) {
1041           break;
1042         }
1043         NekoDirection();
1044         break;
1045   case NEKO_U_MOVE:
1046   case NEKO_D_MOVE:
1047   case NEKO_L_MOVE:
1048   case NEKO_R_MOVE:
1049   case NEKO_UL_MOVE:
1050   case NEKO_UR_MOVE:
1051   case NEKO_DL_MOVE:
1052   case NEKO_DR_MOVE:
1053         NekoX += NekoMoveDx;
1054         NekoY += NekoMoveDy;
1055         NekoDirection();
1056         if ( IsWindowOver() ) {
1057           if ( IsNekoDontMove() ) {
1058                 SetNekoState( NEKO_STOP );
1059           }
1060         }
1061         break;
1062   case NEKO_U_TOGI:
1063   case NEKO_D_TOGI:
1064   case NEKO_L_TOGI:
1065   case NEKO_R_TOGI:
1066         if ( IsNekoMoveStart() ) {
1067           SetNekoState( NEKO_AWAKE );
1068           break;
1069         }
1070         if ( NekoStateCount < NEKO_TOGI_TIME ) {
1071           break;
1072         }
1073         SetNekoState( NEKO_KAKI );
1074         break;
1075   default:
1076         /* Internal Error */
1077         SetNekoState( NEKO_STOP );
1078         break;
1079   }
1080   
1081   Interval();
1082 }
1083
1084
1085 #ifdef  DEBUG
1086 void  DisplayCharacters() {
1087   int           Index;
1088   int           x, y;
1089   
1090   for ( Index = 0, x = 0, y = 0;
1091            BitmapGCDataTable[ Index ].GCCreatePtr != NULL; Index++ ) {
1092         
1093         DrawNeko( x, y, *(BitmapGCDataTable[ Index ].GCCreatePtr) );
1094         XFlush( theDisplay );
1095         
1096         x += BITMAP_WIDTH;
1097         
1098         if ( x > WindowWidth - BITMAP_WIDTH ) {
1099           x = 0;
1100           y += BITMAP_HEIGHT;
1101           if ( y > WindowHeight - BITMAP_HEIGHT) {
1102                 break;
1103           }
1104         }
1105   }
1106 }
1107
1108 #endif  /* DEBUG */
1109
1110 Bool
1111 ProcessKeyPress( XCBKeyPressEvent *theKeyEvent )
1112 {
1113   Bool ReturnState = True;
1114
1115   /* quit on Meta-Q (Alt-Q) */
1116   XCBKEYSYM theKeySym;
1117
1118   /* last param is "int col". What? add enumeration to xcb_keysyms.h */
1119   theKeySym = XCBKeyPressLookupKeysym( theKeySyms, theKeyEvent, 1 );
1120
1121   /* KeySym XK_Q == 'Q' */
1122   if (theKeySym.id == 'Q' && (theKeyEvent->state & XCBModMask1))
1123     ReturnState = False;
1124
1125 #ifdef  DEBUG
1126   if ( EventState == DEBUG_MOVE ) {
1127         switch ( theKeySym ) {
1128         case XK_KP_1:
1129           NekoMoveDx = -(int)( NekoSpeed / sqrt( (double)2 ) );
1130           NekoMoveDy = -NekoMoveDx;
1131           break;
1132         case XK_KP_2:
1133           NekoMoveDx = 0;
1134           NekoMoveDy = (int)NekoSpeed;
1135           break;
1136         case XK_KP_3:
1137           NekoMoveDx = (int)( NekoSpeed / sqrt( (double)2 ) );
1138           NekoMoveDy = NekoMoveDx;
1139           break;
1140         case XK_KP_4:
1141           NekoMoveDx = -(int)NekoSpeed;
1142           NekoMoveDy = 0;
1143           break;
1144         case XK_KP_5:
1145           NekoMoveDx = 0;
1146           NekoMoveDy = 0;
1147           break;
1148         case XK_KP_6:
1149           NekoMoveDx = (int)NekoSpeed;
1150           NekoMoveDy = 0;
1151           break;
1152         case XK_KP_7:
1153           NekoMoveDx = -(int)( NekoSpeed / sqrt( (double)2 ) );
1154           NekoMoveDy = NekoMoveDx;
1155           break;
1156         case XK_KP_8:
1157           NekoMoveDx = 0;
1158           NekoMoveDy = -(int)NekoSpeed;
1159           break;
1160         case XK_KP_9:
1161           NekoMoveDx = (int)( NekoSpeed / sqrt( (double)2 ) );
1162           NekoMoveDy = -NekoMoveDx;
1163           break;
1164         }
1165   }
1166 #endif
1167   
1168   return( ReturnState );
1169 }
1170
1171
1172 void  NekoAdjust(void) {
1173   if ( NekoX < 0 )
1174         NekoX = 0;
1175   else if ( NekoX > WindowWidth - BITMAP_WIDTH )
1176         NekoX = WindowWidth - BITMAP_WIDTH;
1177
1178   if ( NekoY < 0 )
1179         NekoY = 0;
1180   else if ( NekoY > WindowHeight - BITMAP_HEIGHT )
1181         NekoY = WindowHeight - BITMAP_HEIGHT;
1182 }
1183
1184 int IsDeleteMessage(XCBClientMessageEvent *msg)
1185 {
1186         return msg->data.data32[0] == deleteWindowAtom.xid;
1187 }
1188
1189 Bool  ProcessEvent(void) {
1190   XCBGenericEvent *theEvent;
1191   XCBConfigureNotifyEvent *theConfigureNotification;
1192   XCBExposeEvent *theExposure;
1193   XCBButtonPressEvent *theButtonPress;
1194   Bool  ContinueState = True;
1195   int error = 0;
1196   
1197   switch ( EventState ) {
1198   case NORMAL_STATE:
1199     while ( ContinueState &&
1200             NULL != (theEvent = XCBPollForEvent( xc, &error )) ) {  /*while ( XCheckMaskEvent( theDisplay, EVENT_MASK, &theEvent ) ) {*/
1201           switch ( theEvent->response_type & 0x7f ) {
1202           case XCBConfigureNotify:
1203             theConfigureNotification = (XCBConfigureNotifyEvent *)theEvent;
1204                 WindowWidth = theConfigureNotification->width;
1205                 WindowHeight = theConfigureNotification->height;
1206                 WindowPointX = theConfigureNotification->x;
1207                 WindowPointY = theConfigureNotification->y;
1208                 BorderWidth = theConfigureNotification->border_width;
1209                 NekoAdjust();
1210                 break;
1211           case XCBExpose:
1212             theExposure = (XCBExposeEvent *)theEvent;
1213                 if ( theExposure->count == 0 )
1214                   RedrawNeko();
1215                 break;
1216           case XCBMapNotify:
1217                 RedrawNeko();
1218                 break;
1219           case XCBKeyPress:
1220                 ContinueState = ProcessKeyPress( (XCBKeyPressEvent *)theEvent );
1221                 break;
1222           case XCBButtonPress:
1223             theButtonPress = (XCBButtonPressEvent *)theEvent;
1224                 ContinueState = ( theButtonPress->detail.id != 3 );     /* xbutton.button */
1225                 break;
1226           /* new: handle ClientMessage */
1227           case XCBClientMessage:
1228             ContinueState = !IsDeleteMessage((XCBClientMessageEvent *)theEvent);
1229             break;
1230           default:
1231                 /* Unknown Event */
1232                 /*printf("event type:%x\n", (int)theEvent->response_type);*/
1233                 break;
1234           }
1235           free(theEvent);
1236           if (error != 0)
1237                 return False;
1238         } /* end while */
1239         break;
1240 #ifdef  DEBUG
1241   case DEBUG_LIST:
1242         XNextEvent( theDisplay, &theEvent );
1243         switch ( theEvent.type ) {
1244         case ConfigureNotify:
1245           WindowWidth = theEvent.xconfigure.width;
1246           WindowHeight = theEvent.xconfigure.height;
1247           WindowPointX = theEvent.xconfigure.x;
1248           WindowPointY = theEvent.xconfigure.y;
1249           BorderWidth = theEvent.xconfigure.border_width;
1250           break;
1251         case Expose:
1252           if ( theEvent.xexpose.count == 0 )
1253                 DisplayCharacters();
1254           break;
1255         case MapNotify:
1256           DisplayCharacters();
1257           break;
1258         case KeyPress:
1259           ContinueState = ProcessKeyPress( &theEvent );
1260           break;
1261         case ButtonPress:
1262           if ( theEvent.xbutton.button == 3 )
1263                 return( False );
1264           break;
1265         default:
1266           /* Unknown Event */
1267           break;
1268         }
1269         break;
1270   case DEBUG_MOVE:
1271         while ( XCheckMaskEvent( theDisplay, EVENT_MASK, &theEvent ) ) {
1272           switch ( theEvent.type ) {
1273           case ConfigureNotify:
1274                 WindowWidth = theEvent.xconfigure.width;
1275                 WindowHeight = theEvent.xconfigure.height;
1276                 WindowPointX = theEvent.xconfigure.x;
1277                 WindowPointY = theEvent.xconfigure.y;
1278                 BorderWidth = theEvent.xconfigure.border_width;
1279                 NekoAdjust();
1280                 break;
1281           case Expose:
1282                 if ( theEvent.xexpose.count == 0 )
1283                   RedrawNeko();
1284                 break;
1285           case MapNotify:
1286                 RedrawNeko();
1287                 break;
1288           case KeyPress:
1289                 ContinueState = ProcessKeyPress( &theEvent );
1290                 if ( !ContinueState ) {
1291                   return( ContinueState );
1292                 }
1293                 break;
1294           case ButtonPress:
1295                 if ( theEvent.xbutton.button == 3 )
1296                   return( False );
1297                 break;
1298           default:
1299                 /* Unknown Event */
1300                 break;
1301           }
1302         }
1303         break;
1304 #endif
1305   default:
1306         /* Internal Error */
1307         break;
1308   }
1309   
1310   return( ContinueState );
1311 }
1312
1313
1314 void  ProcessNeko(void) {
1315   struct itimerval      Value;
1316   
1317   EventState = NORMAL_STATE;
1318
1319   NekoX = ( WindowWidth - BITMAP_WIDTH / 2 ) / 2;
1320   NekoY = ( WindowHeight - BITMAP_HEIGHT / 2 ) / 2;
1321   
1322   NekoLastX = NekoX;
1323   NekoLastY = NekoY;
1324   
1325   SetNekoState( NEKO_STOP );
1326   
1327   timerclear( &Value.it_interval );
1328   timerclear( &Value.it_value );
1329   
1330   Value.it_interval.tv_usec = IntervalTime;
1331   Value.it_value.tv_usec = IntervalTime;
1332   
1333   setitimer( ITIMER_REAL, &Value, 0 );
1334   
1335   do {
1336         NekoThinkDraw();
1337   } while ( ProcessEvent() );
1338 }
1339
1340 #ifdef  DEBUG
1341
1342 void  NekoList() {
1343   EventState = DEBUG_LIST;
1344   
1345   fprintf( stderr, "\n" );
1346   fprintf( stderr, "G-0lMw$rI=<($7$^$9!#(Quit !D Alt-Q)\n" );
1347   fprintf( stderr, "\n" );
1348   
1349   XSelectInput( theDisplay, theWindow, EVENT_MASK );
1350   
1351   while ( ProcessEvent() );
1352 }
1353
1354
1355 void  NekoMoveTest() {
1356   struct itimerval      Value;
1357   
1358   EventState = DEBUG_MOVE;
1359   
1360   NekoX = ( WindowWidth - BITMAP_WIDTH / 2 ) / 2;
1361   NekoY = ( WindowHeight - BITMAP_HEIGHT / 2 ) / 2;
1362   
1363   NekoLastX = NekoX;
1364   NekoLastY = NekoY;
1365   
1366   SetNekoState( NEKO_STOP );
1367   
1368   timerclear( &Value.it_interval );
1369   timerclear( &Value.it_value );
1370   
1371   Value.it_interval.tv_usec = IntervalTime;
1372   Value.it_value.tv_usec = IntervalTime;
1373   
1374   setitimer( ITIMER_REAL, &Value, 0 );
1375   
1376   fprintf( stderr, "\n" );
1377   fprintf( stderr, "G-$N0\F0%F%9%H$r9T$$$^$9!#(Quit !D Alt-Q)\n" );
1378   fprintf( stderr, "\n" );
1379   fprintf( stderr, "\t%-!<%Q%C%I>e$N%F%s%-!<$GG-$r0\F0$5$;$F2<$5$$!#\n" );
1380   fprintf( stderr, "\t(M-8z$J%-!<$O#1!A#9$G$9!#)\n" );
1381   fprintf( stderr, "\n" );
1382   
1383   do {
1384         NekoThinkDraw();
1385   } while ( ProcessEvent() );
1386 }
1387
1388
1389 void  ProcessDebugMenu() {
1390   int           UserSelectNo = 0;
1391   char  UserAnswer[ BUFSIZ ];
1392   
1393   fprintf( stderr, "\n" );
1394   fprintf( stderr, "!Zxneko %G%P%C%0%a%K%e!<![\n" );
1395   
1396   while ( !( UserSelectNo >= 1 && UserSelectNo <= 2 ) ) {
1397         fprintf( stderr, "\n" );
1398         fprintf( stderr, "\t1)!!G-%-%c%i%/%?!<0lMwI=<(\n" );
1399         fprintf( stderr, "\t2)!!G-0\F0%F%9%H\n" );
1400         fprintf( stderr, "\n" );
1401         fprintf( stderr, "Select: " );
1402
1403         fgets( UserAnswer, sizeof( UserAnswer ), stdin );
1404         
1405         UserSelectNo = atoi( UserAnswer );
1406         
1407         if ( !( UserSelectNo >= 1 && UserSelectNo <= 2 ) ) {
1408           fprintf( stderr, "\n" );
1409           fprintf( stderr, "@5$7$$HV9f$rA*Br$7$F2<$5$$!#\n" );
1410         }
1411   }
1412   
1413   switch ( UserSelectNo ) {
1414   case 1:
1415         NekoList();
1416         break;
1417   case 2:
1418         NekoMoveTest();
1419         break;
1420   default:
1421         /* Internal Error */
1422         break;
1423   }
1424   
1425   fprintf( stderr, "%F%9%H=*N;!#\n" );
1426   fprintf( stderr, "\n" );
1427 }
1428
1429 #endif  /* DEBUG */
1430
1431 void  NullFunction(int ignored)
1432 {
1433   /* signal( SIGALRM, NullFunction ); */
1434 }
1435
1436
1437 void  Usage(void) {
1438   fprintf( stderr,
1439                   "Usage: %s [-display <display>] [-geometry <geometry>] \\\n",
1440                   ProgramName );
1441   fprintf( stderr, "\t[-bg <background>] [-fg <foreground>] \\\n" );
1442   fprintf( stderr, "\t[-title <title>] [-name <title>] [-iconic] \\\n" );
1443   fprintf( stderr, "\t[-speed <speed>] [-time <time>] [-root] [-help]\n" );
1444 }
1445
1446
1447 Bool
1448 GetArguments( int argc, char *argv[],
1449         char *theDisplayName, char *theGeometry, char **theTitle,
1450         double *NekoSpeed, long *IntervalTime )
1451 {
1452   int           ArgCounter;
1453   Bool  iconicState;
1454   
1455   theDisplayName[ 0 ] = '\0';
1456   theGeometry[ 0 ] = '\0';
1457   
1458   iconicState = False;
1459   
1460   for ( ArgCounter = 0; ArgCounter < argc; ArgCounter++ ) {
1461         if ( strncmp( argv[ ArgCounter ], "-h", 2 ) == 0 ) {
1462           Usage();
1463           exit( 0 );
1464         } else if ( strcmp( argv[ ArgCounter ], "-display" ) == 0 ) {
1465           ArgCounter++;
1466           if ( ArgCounter < argc ) {
1467                 strcpy( theDisplayName, argv[ ArgCounter ] );
1468           } else {
1469                 fprintf( stderr, "%s: -display option error.\n", ProgramName );
1470                 exit( 1 );
1471           }
1472         } else if ( strncmp( argv[ ArgCounter ], "-geom", 5 ) == 0 ) {
1473           ArgCounter++;
1474           if ( ArgCounter < argc ) {
1475                 strcpy( theGeometry, argv[ ArgCounter ] );
1476           } else {
1477                 fprintf( stderr,
1478                                 "%s: -geometry option error.\n", ProgramName );
1479                 exit( 1 );
1480           }
1481         } else if ( ( strcmp( argv[ ArgCounter ], "-title" ) == 0 )
1482                            || ( strcmp( argv[ ArgCounter ], "-name" ) == 0 ) ) {
1483           ArgCounter++;
1484           if ( ArgCounter < argc ) {
1485             *theTitle = argv[ ArgCounter ];
1486           } else {
1487                 fprintf( stderr, "%s: -title option error.\n", ProgramName );
1488                 exit( 1 );
1489           }
1490         } else if ( strcmp( argv[ ArgCounter ], "-iconic" ) == 0 ) {
1491           iconicState = True;
1492         } else if ( strcmp( argv[ ArgCounter ], "-speed" ) == 0 ) {
1493           ArgCounter++;
1494           if ( ArgCounter < argc ) {
1495                 *NekoSpeed = atof( argv[ ArgCounter ] );
1496           } else {
1497                 fprintf( stderr, "%s: -speed option error.\n", ProgramName );
1498                 exit( 1 );
1499           }
1500         } else if ( strcmp( argv[ ArgCounter ], "-fg" ) == 0 ) {
1501           ArgCounter++;
1502           if ( ArgCounter < argc ) {
1503                 fgColor = argv[ArgCounter];
1504           } else {
1505                 fprintf( stderr, "%s: -fg option error.\n", ProgramName );
1506                 exit( 1 );
1507           }
1508         } else if ( strcmp( argv[ ArgCounter ], "-bg" ) == 0 ) {
1509           ArgCounter++;
1510           if ( ArgCounter < argc ) {
1511                 bgColor = argv[ArgCounter];
1512           } else {
1513                 fprintf( stderr, "%s: -bg option error.\n", ProgramName );
1514                 exit( 1 );
1515           }
1516         } else if ( strcmp( argv[ ArgCounter ], "-time" ) == 0 ) {
1517           ArgCounter++;
1518           if ( ArgCounter < argc ) {
1519                 *IntervalTime = atol( argv[ ArgCounter ] );
1520           } else {
1521                 fprintf( stderr, "%s: -time option error.\n", ProgramName );
1522                 exit( 1 );
1523           }
1524         } else if ( strcmp( argv[ ArgCounter ], "-root" ) == 0 ) {
1525           useRoot = 1;
1526         } else {
1527           fprintf( stderr,
1528                           "%s: Unknown option \"%s\".\n", ProgramName,
1529                           argv[ ArgCounter ] );
1530           Usage();
1531           exit( 1 );
1532         }
1533   }
1534   
1535   return( iconicState );
1536 }
1537
1538 void UndefineCursor( XCBConnection *c, XCBWINDOW w)
1539 {
1540         CARD32 none[] = { XCBNone };
1541         XCBChangeWindowAttributes( c, w, XCBCWCursor, none );
1542 }
1543
1544 int
1545 main( int argc, char *argv[] )
1546 {
1547   Bool  iconicState;
1548   char  theDisplayName[ DIRNAMELEN ];
1549   char  theGeometry[ DIRNAMELEN ];
1550   char  *theTitle = "";
1551   
1552   ProgramName = argv[ 0 ];
1553   
1554   argc--;
1555   argv++;
1556   
1557   useRoot = 0;
1558   fgColor = bgColor = (char *) NULL;
1559   
1560   iconicState = GetArguments( argc, argv, theDisplayName, theGeometry,
1561                                                          &theTitle, &NekoSpeed, &IntervalTime );
1562   
1563   if (theTitle[0] == 0) theTitle = ProgramName;
1564   InitScreen( theDisplayName, theGeometry, theTitle, iconicState );
1565   
1566   signal( SIGALRM, NullFunction );
1567   
1568   SinPiPer8Times3 = sin( PI_PER8 * (double)3 );
1569   SinPiPer8 = sin( PI_PER8 );
1570
1571 #ifndef DEBUG
1572   ProcessNeko();
1573 #else
1574   ProcessDebugMenu();
1575 #endif
1576
1577   UndefineCursor( xc, theWindow );
1578   XCBDisconnect( xc );
1579   exit( 0 );
1580 }