Support WM_DELETE_WINDOW, WMHints.
[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  theBlackPixel;
147 unsigned long  theWhitePixel;
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 bufLen = (w+7)/8*h;
427   BYTE buf[1024];
428   if (XCBGetSetup(c)->bitmap_format_scanline_unit == 32 &&
429       XCBGetSetup(c)->bitmap_format_bit_order == XCBImageOrderMSBFirst &&
430       XCBGetSetup(c)->image_byte_order == XCBImageOrderMSBFirst)
431   {
432     long bpl = (w+7)/8;
433     long pad = XCBGetSetup(c)->bitmap_format_scanline_pad;
434     long bpd = ROUNDUP(w, pad)>>3;
435     SwapBits((unsigned char *)data, (unsigned char *)buf, bpl, bpl, bpd, h);
436     bufLen = bpd * h;
437   }
438   else
439     memcpy(buf, data, bufLen);
440
441   /* note: CBfD uses XYPixmap, but CPfBD uses XYBitmap
442            there shouldn't be a difference when depth==1,
443            but the neko images are corrupt if using XYPixmap */
444   CARD8 format = (depth==1 ? XCBImageFormatXYPixmap : XCBImageFormatXYBitmap);
445   
446   /* PutImage.c: left_pad = (image->xoffset + req->xoffset) & (dpy->bitmap_unit-1)
447        screen->bitmap_format_scanline_unit
448        left_pad = (0 + 0) & (32 - 1) = 0 */
449
450   XCBPutImage( c, format, drawable, gc,
451         w, h, 0, 0,
452         0, 1,           /* left_pad, depth */
453         bufLen, buf);
454
455 #if DEBUG
456   XCBGenericError *error = NULL;
457   XCBSync( c, &error );
458   if (error) {
459     printf("error code %d", (int)error->error_code);
460     free(error);
461   }
462 #endif
463
464   XCBFreeGC( c, gc );
465   
466   /* later: XCBFreePixmap( c, bitmap ); */
467   return bitmap;
468 }
469
470 XCBPIXMAP CreateBitmapFromData(XCBConnection *c, XCBWINDOW window,
471         char *data, CARD16 w, CARD16 h)
472 {
473         CARD32 depth = 1;
474         return CreatePixmapFromBitmapData(c, window, data, w, h, 0, 0, depth);
475 }
476
477 void  InitBitmapAndGCs(void) {
478   BitmapGCData  *BitmapGCDataTablePtr;
479   CARD32 theGCValues[5];
480   XCBDRAWABLE drawable;   drawable.window = theWindow;
481
482   theGCValues[0] = XCBGXcopy;
483
484   theGCValues[1] = theBlackPixel;
485   theGCValues[2] = theWhitePixel;
486
487   theGCValues[3] = XCBFillStyleTiled;
488   
489   /* TODO: latency: make all the bitmaps, then all the contexts? */
490
491   for ( BitmapGCDataTablePtr = BitmapGCDataTable;
492         BitmapGCDataTablePtr->GCCreatePtr != NULL;
493         BitmapGCDataTablePtr++ ) {
494
495         *(BitmapGCDataTablePtr->BitmapCreatePtr)
496           = CreatePixmapFromBitmapData( xc, theScreen->root,
497                 BitmapGCDataTablePtr->PixelPattern,
498                 BitmapGCDataTablePtr->PixelWidth,
499                 BitmapGCDataTablePtr->PixelHeight,
500                 theBlackPixel, theWhitePixel, theScreen->root_depth);
501
502         theGCValues[4] = BitmapGCDataTablePtr->BitmapCreatePtr->xid; /* tile */
503         
504         *(BitmapGCDataTablePtr->GCCreatePtr) = XCBGCONTEXTNew( xc );
505         XCBCreateGC( xc, *(BitmapGCDataTablePtr->GCCreatePtr), drawable,
506                   XCBGCFunction | XCBGCForeground | XCBGCBackground |
507                   XCBGCFillStyle | XCBGCTile,
508                   theGCValues );
509   }
510   
511   /* later: XCBFreePixmap( c, bitmap ); */
512   /* later: XCBFreeGC( c, gc ); */
513 }
514
515 XCBATOM
516 GetAtom(XCBConnection *c, const char *atomName)
517 {
518         XCBATOM atom = { XCBNone };
519         XCBInternAtomRep *r = XCBInternAtomReply(c,
520                 XCBInternAtom(c, 0, strlen(atomName), atomName), NULL);
521         if (r) {
522                 atom = r->atom;
523                 free(r);
524         }
525         return atom;
526 }
527
528 void
529 InitScreen( char *DisplayName, char *theGeometry, char *theTitle, Bool iconicState )
530 {
531   XCBPIXMAP             theCursorSource;
532   XCBPIXMAP             theCursorMask;
533   unsigned int   theDepth;
534   XCBCOLORMAP           theColormap;
535   int screen;
536   
537   if ( ( xc = XCBConnect( DisplayName, &screen ) ) == NULL ) {
538         fprintf( stderr, "%s: Can't open connection", ProgramName );
539         if ( DisplayName != NULL )
540           fprintf( stderr, " %s.\n", DisplayName );
541         else
542           fprintf( stderr, ".\n" );
543         exit( 1 );
544   }
545   
546   theScreen = XCBAuxGetScreen(xc, screen);
547   if (theScreen == NULL) {
548         fprintf( stderr, "%s: Can't get default screen", ProgramName );
549         exit( 1 );
550   }
551   
552   theDepth    = theScreen->root_depth;  /* DefaultDepth */
553   theColormap = theScreen->default_colormap;
554
555   WindowPointX = DEFAULT_WIN_X;
556   WindowPointY = DEFAULT_WIN_Y;
557   WindowWidth  = WINDOW_WIDTH;
558   WindowHeight = WINDOW_HEIGHT;
559
560 #ifdef TODO  
561   int                   GeometryStatus;
562   GeometryStatus = XParseGeometry( theGeometry,
563                                                                   &WindowPointX, &WindowPointY,
564                                                                   &WindowWidth, &WindowHeight );
565 #endif
566   
567   theCursorSource = CreateBitmapFromData( xc,
568                                                                                   theScreen->root,
569                                                                                   cursor_bits,
570                                                                                   cursor_width,
571                                                                                   cursor_height );
572   
573   theCursorMask = CreateBitmapFromData( xc,
574                                                                                 theScreen->root,
575                                                                                 cursor_mask_bits,
576                                                                                 cursor_mask_width,
577                                                                                 cursor_mask_height );
578
579
580   if ( bgColor == NULL) bgColor = "white";
581   if ( fgColor == NULL) fgColor = "black";
582   
583   XCBAllocNamedColorCookie bgCookie = XCBAllocNamedColor ( xc,
584                 theColormap,  strlen(bgColor), bgColor );
585
586   XCBAllocNamedColorCookie fgCookie = XCBAllocNamedColor ( xc,
587                 theColormap,  strlen(fgColor), fgColor );
588                 
589   XCBAllocNamedColorRep *bgRep = XCBAllocNamedColorReply( xc, bgCookie, 0 );
590   if (!bgRep) {
591         fprintf( stderr,
592                         "%s: Can't allocate the background color %s.\n", ProgramName, bgColor );
593         exit( 1 );
594   }
595   theWhitePixel = bgRep->pixel;
596
597   XCBAllocNamedColorRep *fgRep = XCBAllocNamedColorReply( xc, fgCookie, 0 );
598   if (!fgRep) {
599         fprintf( stderr,
600                         "%s: Can't allocate the foreground color %s.\n", ProgramName, fgColor );
601         exit( 1 );
602   }
603   theBlackPixel = fgRep->pixel;
604   
605   theCursor = XCBCURSORNew( xc );
606   XCBCreateCursor ( xc, theCursor, theCursorSource, theCursorMask,
607         fgRep->visual_red, fgRep->visual_green, fgRep->visual_blue,
608         bgRep->visual_red, bgRep->visual_green, bgRep->visual_blue,
609         cursor_x_hot, cursor_y_hot );
610
611   free(bgRep);
612   free(fgRep);
613
614   if ( useRoot ) {
615     CARD32 rootAttributes[] = { EVENT_MASK_ROOT, theCursor.xid };
616         theWindow = theScreen->root;
617         XCBChangeWindowAttributes(xc, theWindow,
618                 XCBCWEventMask | XCBCWCursor, rootAttributes );
619   }
620   else {
621         XCBPIXMAP                theIconPixmap;
622
623         CARD32 theWindowAttributes[] = {
624                 theWhitePixel, /* background */
625                 theBlackPixel, /* border */
626                 False,         /* override_redirect */
627                 EVENT_MASK,
628                 theCursor.xid };
629
630         unsigned long theWindowMask = XCBCWBackPixel | XCBCWBorderPixel |
631           XCBCWOverrideRedirect | XCBCWEventMask | XCBCWCursor;
632         
633         theWindow = XCBWINDOWNew( xc );
634         XCBCreateWindow( xc,
635                 theDepth,
636                 theWindow,
637                 theScreen->root,
638                 WindowPointX, WindowPointY,
639                 WindowWidth, WindowHeight,
640                 BorderWidth,
641                 XCBWindowClassInputOutput,
642                 theScreen->root_visual, /* CopyFromParent */
643                 theWindowMask, theWindowAttributes );
644
645         /* new: obey the window-delete protocol, look for XCBClientMessage */
646         deleteWindowAtom = GetAtom(xc, "WM_DELETE_WINDOW");
647         SetWMProtocols( xc, theWindow, 1, &deleteWindowAtom );
648
649         theIconPixmap = CreateBitmapFromData( xc, theWindow,
650                                                                                   icon_bits, icon_width, icon_height );
651 #ifdef TODO_ICCCM
652         theWMHints.icon_pixmap = theIconPixmap;
653         
654         if ( iconicState )
655           theWMHints.initial_state = IconicState;
656         else
657           theWMHints.initial_state = NormalState;
658         
659         theWMHints.flags = IconPixmapHint | StateHint;
660         
661         XSetWMHints( theDisplay, theWindow, &theWMHints );
662 #else
663     /* Um... there is no function to send the hints...
664         WMHints              theWMHints;
665         WMHintsSetIconPixmap( &theHints, theIconPixmap );
666
667         if ( iconicState )
668           WMHintsSetIconic( &theHints );
669         else
670           WMHintsSetNormal( &theHints );
671           
672         Now what?
673     */
674 #endif
675
676         /* why hide the structure? */
677         SizeHints *theSizeHints = AllocSizeHints();
678
679         /* need enum for second param (user specified) */
680         SizeHintsSetPosition(theSizeHints, 0, WindowPointX, WindowPointY);
681         SizeHintsSetSize(theSizeHints, 0, WindowWidth, WindowHeight);
682
683         SetWMNormalHints(xc, theWindow, theSizeHints);
684
685         FreeSizeHints(theSizeHints);
686
687         /* Um, why do I have to specify the encoding in this API? */
688         SetWMName( xc, theWindow, STRING, strlen(theTitle), theTitle );
689         SetWMIconName( xc, theWindow, STRING, strlen(theTitle), theTitle );
690
691         XCBMapWindow( xc, theWindow );
692
693   }
694   
695 #ifdef TODO
696   /* is this really necessary? */
697   XSetWindowBackground( theDisplay, theWindow, theWhitePixel );
698   XClearWindow( theDisplay, theWindow );
699   XFlush( theDisplay );
700
701   XCBWINDOW             theRoot;
702   XGetGeometry( theDisplay, theWindow, &theRoot,
703                            &WindowPointX, &WindowPointY, &WindowWidth, &WindowHeight,
704                            &BorderWidth, &theDepth );
705 #endif
706
707   InitBitmapAndGCs();
708
709   XCBFlush(xc);
710
711   /* latency: ask for keysyms now, and receive them later */
712   theKeySyms = XCBKeySymbolsAlloc( xc );
713
714   /* later: XCBKeySymbolsFree( keysyms ); */
715   /* later: XCBRefreshKeyboardMapping ( keysyms, mappingEvent ); */
716 }
717
718
719 void  Interval(void) {
720   pause();
721 }
722
723
724 void  TickCount(void) {
725   if ( ++NekoTickCount >= MAX_TICK )
726         NekoTickCount = 0;
727   
728   if ( NekoTickCount % 2 == 0 )
729         if ( NekoStateCount < MAX_TICK )
730           NekoStateCount++;
731 }
732
733
734 void
735 SetNekoState( int SetValue )
736 {
737   NekoTickCount = 0;
738   NekoStateCount = 0;
739   
740   NekoState = SetValue;
741   
742 #ifdef  DEBUG
743   switch ( NekoState ) {
744   case NEKO_STOP:
745   case NEKO_JARE:
746   case NEKO_KAKI:
747   case NEKO_AKUBI:
748   case NEKO_SLEEP:
749   case NEKO_U_TOGI:
750   case NEKO_D_TOGI:
751   case NEKO_L_TOGI:
752   case NEKO_R_TOGI:
753         NekoMoveDx = NekoMoveDy = 0;
754         break;
755   default:
756         break;
757   }
758 #endif
759 }
760
761 /* FillRct.c */
762 /*   Xlib does merging of requests, but the Flush and frequent DrawGC changes
763      defeat this mechanism */
764
765 void
766 DrawNeko( int x, int y, XCBGCONTEXT DrawGC )
767 {
768   XCBDRAWABLE drawable;  drawable.window = theWindow;
769   XCBRECTANGLE rect = { NekoLastX, NekoLastY, BITMAP_WIDTH, BITMAP_HEIGHT };
770
771   if ( (x != NekoLastX || y != NekoLastY) && (EventState != DEBUG_LIST) )
772   {
773         XCBPolyFillRectangle( xc, drawable, SpaceGC, 1, &rect );
774         rect.x = x; rect.y = y;
775
776   }
777
778   CARD32 originMask = XCBGCTileStippleOriginX | XCBGCTileStippleOriginY;
779   CARD32 origin[2] = { x, y };
780   XCBChangeGC( xc, DrawGC, originMask, origin );
781   /* XSetTSOrigin( theDisplay, DrawGC, x, y ); */
782
783   XCBPolyFillRectangle( xc, drawable, DrawGC, 1, &rect );
784
785   XCBFlush( xc );
786
787   NekoLastX = x;
788   NekoLastY = y;
789   
790   NekoLastGC = DrawGC;
791 }
792
793
794 void  RedrawNeko(void) {
795   XCBDRAWABLE drawable;  drawable.window = theWindow;
796   XCBRECTANGLE rect = { NekoLastX, NekoLastY, BITMAP_WIDTH, BITMAP_HEIGHT };
797
798   XCBPolyFillRectangle( xc, drawable, NekoLastGC, 1, &rect );
799
800   XCBFlush( xc );
801 }
802
803
804
805 void  NekoDirection(void) {
806   int                   NewState;
807   double                LargeX, LargeY;
808   double                Length;
809   double                SinTheta;
810   
811   if ( NekoMoveDx == 0 && NekoMoveDy == 0 ) {
812         NewState = NEKO_STOP;
813   } else {
814         LargeX = (double)NekoMoveDx;
815         LargeY = (double)(-NekoMoveDy);
816         Length = sqrt( LargeX * LargeX + LargeY * LargeY );
817         SinTheta = LargeY / Length;
818         
819         if ( NekoMoveDx > 0 ) {
820           if ( SinTheta > SinPiPer8Times3 ) {
821                 NewState = NEKO_U_MOVE;
822           } else if ( ( SinTheta <= SinPiPer8Times3 )
823                                  && ( SinTheta > SinPiPer8 ) ) {
824                 NewState = NEKO_UR_MOVE;
825           } else if ( ( SinTheta <= SinPiPer8 )
826                                  && ( SinTheta > -( SinPiPer8 ) ) ) {
827                 NewState = NEKO_R_MOVE;
828           } else if ( ( SinTheta <= -( SinPiPer8 ) )
829                                  && ( SinTheta > -( SinPiPer8Times3 ) ) ) {
830                 NewState = NEKO_DR_MOVE;
831           } else {
832                 NewState = NEKO_D_MOVE;
833           }
834         } else {
835           if ( SinTheta > SinPiPer8Times3 ) {
836                 NewState = NEKO_U_MOVE;
837           } else if ( ( SinTheta <= SinPiPer8Times3 )
838                                  && ( SinTheta > SinPiPer8 ) ) {
839                 NewState = NEKO_UL_MOVE;
840           } else if ( ( SinTheta <= SinPiPer8 )
841                                  && ( SinTheta > -( SinPiPer8 ) ) ) {
842                 NewState = NEKO_L_MOVE;
843           } else if ( ( SinTheta <= -( SinPiPer8 ) )
844                                  && ( SinTheta > -( SinPiPer8Times3 ) ) ) {
845                 NewState = NEKO_DL_MOVE;
846           } else {
847                 NewState = NEKO_D_MOVE;
848           }
849         }
850   }
851   
852   if ( NekoState != NewState ) {
853         SetNekoState( NewState );
854   }
855 }
856
857
858 Bool  IsWindowOver(void) {
859   Bool  ReturnValue = False;
860   
861   if ( NekoY <= 0 ) {
862         NekoY = 0;
863         ReturnValue = True;
864   } else if ( NekoY >= WindowHeight - BITMAP_HEIGHT ) {
865         NekoY = WindowHeight - BITMAP_HEIGHT;
866         ReturnValue = True;
867   }
868   if ( NekoX <= 0 ) {
869         NekoX = 0;
870         ReturnValue = True;
871   } else if ( NekoX >= WindowWidth - BITMAP_WIDTH ) {
872         NekoX = WindowWidth - BITMAP_WIDTH;
873         ReturnValue = True;
874   }
875   
876   return( ReturnValue );
877 }
878
879
880 Bool  IsNekoDontMove(void) {
881   return( NekoX == NekoLastX  &&  NekoY == NekoLastY );
882 }
883
884
885 Bool  IsNekoMoveStart(void) {
886 #ifndef DEBUG
887   if ( (PrevMouseX >= MouseX - IDLE_SPACE
888                 && PrevMouseX <= MouseX + IDLE_SPACE) &&
889           (PrevMouseY >= MouseY - IDLE_SPACE 
890            && PrevMouseY <= MouseY + IDLE_SPACE) )
891         return( False );
892   else
893         return( True );
894 #else
895   if ( NekoMoveDx == 0 && NekoMoveDy == 0 )
896         return( False );
897   else
898         return( True );
899 #endif
900 }
901
902 #ifndef DEBUG
903 void  CalcDxDy(void) {
904   int                   RelativeX, RelativeY;
905   double                LargeX, LargeY;
906   double                DoubleLength, Length;
907   
908   /* TODO: replace query with pointer motion notification? */
909
910   XCBQueryPointerRep *reply = XCBQueryPointerReply( xc,
911         XCBQueryPointer( xc, theWindow ), NULL);
912         
913   RelativeX = reply->win_x;
914   RelativeY = reply->win_y;
915   
916   free(reply);
917
918   PrevMouseX = MouseX;
919   PrevMouseY = MouseY;
920   
921   MouseX = RelativeX;
922   MouseY = RelativeY;
923   
924   LargeX = (double)( MouseX - NekoX - BITMAP_WIDTH / 2 );
925   LargeY = (double)( MouseY - NekoY - BITMAP_HEIGHT );
926   
927   DoubleLength = LargeX * LargeX + LargeY * LargeY;
928   
929   if ( DoubleLength != (double)0 ) {
930         Length = sqrt( DoubleLength );
931         if ( Length <= NekoSpeed ) {
932           NekoMoveDx = (int)LargeX;
933           NekoMoveDy = (int)LargeY;
934         } else {
935           NekoMoveDx = (int)( ( NekoSpeed * LargeX ) / Length );
936           NekoMoveDy = (int)( ( NekoSpeed * LargeY ) / Length );
937         }
938   } else {
939         NekoMoveDx = NekoMoveDy = 0;
940   }
941 }
942 #endif
943
944 void  NekoThinkDraw(void) {
945 #ifndef DEBUG
946   CalcDxDy();
947 #endif
948
949   if ( NekoState != NEKO_SLEEP ) {
950         DrawNeko( NekoX, NekoY,
951                          NekoTickCount % 2 == 0 ?
952                          *(AnimationPattern[ NekoState ].TickEvenGCPtr) :
953                          *(AnimationPattern[ NekoState ].TickOddGCPtr) );
954   } else {
955         DrawNeko( NekoX, NekoY,
956                          NekoTickCount % 8 <= 3 ?
957                          *(AnimationPattern[ NekoState ].TickEvenGCPtr) :
958                          *(AnimationPattern[ NekoState ].TickOddGCPtr) );
959   }
960   
961   TickCount();
962   
963   switch ( NekoState ) {
964   case NEKO_STOP:
965         if ( IsNekoMoveStart() ) {
966           SetNekoState( NEKO_AWAKE );
967           break;
968         }
969         if ( NekoStateCount < NEKO_STOP_TIME ) {
970           break;
971         }
972         if ( NekoMoveDx < 0 && NekoX <= 0 ) {
973           SetNekoState( NEKO_L_TOGI );
974         } else if ( NekoMoveDx > 0 && NekoX >= WindowWidth - BITMAP_WIDTH ) {
975           SetNekoState( NEKO_R_TOGI );
976         } else if ( NekoMoveDy < 0 && NekoY <= 0 ) {
977           SetNekoState( NEKO_U_TOGI );
978         } else if ( NekoMoveDy > 0 && NekoY >= WindowHeight - BITMAP_HEIGHT ) {
979           SetNekoState( NEKO_D_TOGI );
980         } else {
981           SetNekoState( NEKO_JARE );
982         }
983         break;
984   case NEKO_JARE:
985         if ( IsNekoMoveStart() ) {
986           SetNekoState( NEKO_AWAKE );
987           break;
988         }
989         if ( NekoStateCount < NEKO_JARE_TIME ) {
990           break;
991         }
992         SetNekoState( NEKO_KAKI );
993         break;
994   case NEKO_KAKI:
995         if ( IsNekoMoveStart() ) {
996           SetNekoState( NEKO_AWAKE );
997           break;
998         }
999         if ( NekoStateCount < NEKO_KAKI_TIME ) {
1000           break;
1001         }
1002         SetNekoState( NEKO_AKUBI );
1003         break;
1004   case NEKO_AKUBI:
1005         if ( IsNekoMoveStart() ) {
1006           SetNekoState( NEKO_AWAKE );
1007           break;
1008         }
1009         if ( NekoStateCount < NEKO_AKUBI_TIME ) {
1010           break;
1011         }
1012         SetNekoState( NEKO_SLEEP );
1013         break;
1014   case NEKO_SLEEP:
1015         if ( IsNekoMoveStart() ) {
1016           SetNekoState( NEKO_AWAKE );
1017           break;
1018         }
1019         break;
1020   case NEKO_AWAKE:
1021         if ( NekoStateCount < NEKO_AWAKE_TIME ) {
1022           break;
1023         }
1024         NekoDirection();
1025         break;
1026   case NEKO_U_MOVE:
1027   case NEKO_D_MOVE:
1028   case NEKO_L_MOVE:
1029   case NEKO_R_MOVE:
1030   case NEKO_UL_MOVE:
1031   case NEKO_UR_MOVE:
1032   case NEKO_DL_MOVE:
1033   case NEKO_DR_MOVE:
1034         NekoX += NekoMoveDx;
1035         NekoY += NekoMoveDy;
1036         NekoDirection();
1037         if ( IsWindowOver() ) {
1038           if ( IsNekoDontMove() ) {
1039                 SetNekoState( NEKO_STOP );
1040           }
1041         }
1042         break;
1043   case NEKO_U_TOGI:
1044   case NEKO_D_TOGI:
1045   case NEKO_L_TOGI:
1046   case NEKO_R_TOGI:
1047         if ( IsNekoMoveStart() ) {
1048           SetNekoState( NEKO_AWAKE );
1049           break;
1050         }
1051         if ( NekoStateCount < NEKO_TOGI_TIME ) {
1052           break;
1053         }
1054         SetNekoState( NEKO_KAKI );
1055         break;
1056   default:
1057         /* Internal Error */
1058         SetNekoState( NEKO_STOP );
1059         break;
1060   }
1061   
1062   Interval();
1063 }
1064
1065
1066 #ifdef  DEBUG
1067 void  DisplayCharacters() {
1068   int           Index;
1069   int           x, y;
1070   
1071   for ( Index = 0, x = 0, y = 0;
1072            BitmapGCDataTable[ Index ].GCCreatePtr != NULL; Index++ ) {
1073         
1074         DrawNeko( x, y, *(BitmapGCDataTable[ Index ].GCCreatePtr) );
1075         XFlush( theDisplay );
1076         
1077         x += BITMAP_WIDTH;
1078         
1079         if ( x > WindowWidth - BITMAP_WIDTH ) {
1080           x = 0;
1081           y += BITMAP_HEIGHT;
1082           if ( y > WindowHeight - BITMAP_HEIGHT) {
1083                 break;
1084           }
1085         }
1086   }
1087 }
1088
1089 #endif  /* DEBUG */
1090
1091 Bool
1092 ProcessKeyPress( XCBKeyPressEvent *theKeyEvent )
1093 {
1094   Bool ReturnState = True;
1095
1096   /* quit on Meta-Q (Alt-Q) */
1097   XCBKEYSYM theKeySym;
1098
1099   /* last param is "int col". What? add enumeration to xcb_keysyms.h */
1100   theKeySym = XCBKeyPressLookupKeysym( theKeySyms, theKeyEvent, 1 );
1101
1102   /* KeySym XK_Q == 'Q' */
1103   if (theKeySym.id == 'Q' && (theKeyEvent->state & XCBModMask1))
1104     ReturnState = False;
1105
1106 #ifdef  DEBUG
1107   if ( EventState == DEBUG_MOVE ) {
1108         switch ( theKeySym ) {
1109         case XK_KP_1:
1110           NekoMoveDx = -(int)( NekoSpeed / sqrt( (double)2 ) );
1111           NekoMoveDy = -NekoMoveDx;
1112           break;
1113         case XK_KP_2:
1114           NekoMoveDx = 0;
1115           NekoMoveDy = (int)NekoSpeed;
1116           break;
1117         case XK_KP_3:
1118           NekoMoveDx = (int)( NekoSpeed / sqrt( (double)2 ) );
1119           NekoMoveDy = NekoMoveDx;
1120           break;
1121         case XK_KP_4:
1122           NekoMoveDx = -(int)NekoSpeed;
1123           NekoMoveDy = 0;
1124           break;
1125         case XK_KP_5:
1126           NekoMoveDx = 0;
1127           NekoMoveDy = 0;
1128           break;
1129         case XK_KP_6:
1130           NekoMoveDx = (int)NekoSpeed;
1131           NekoMoveDy = 0;
1132           break;
1133         case XK_KP_7:
1134           NekoMoveDx = -(int)( NekoSpeed / sqrt( (double)2 ) );
1135           NekoMoveDy = NekoMoveDx;
1136           break;
1137         case XK_KP_8:
1138           NekoMoveDx = 0;
1139           NekoMoveDy = -(int)NekoSpeed;
1140           break;
1141         case XK_KP_9:
1142           NekoMoveDx = (int)( NekoSpeed / sqrt( (double)2 ) );
1143           NekoMoveDy = -NekoMoveDx;
1144           break;
1145         }
1146   }
1147 #endif
1148   
1149   return( ReturnState );
1150 }
1151
1152
1153 void  NekoAdjust(void) {
1154   if ( NekoX < 0 )
1155         NekoX = 0;
1156   else if ( NekoX > WindowWidth - BITMAP_WIDTH )
1157         NekoX = WindowWidth - BITMAP_WIDTH;
1158
1159   if ( NekoY < 0 )
1160         NekoY = 0;
1161   else if ( NekoY > WindowHeight - BITMAP_HEIGHT )
1162         NekoY = WindowHeight - BITMAP_HEIGHT;
1163 }
1164
1165 int IsDeleteMessage(XCBClientMessageEvent *msg)
1166 {
1167         return msg->data.data32[0] == deleteWindowAtom.xid;
1168 }
1169
1170 Bool  ProcessEvent(void) {
1171   XCBGenericEvent *theEvent;
1172   XCBConfigureNotifyEvent *theConfigureNotification;
1173   XCBExposeEvent *theExposure;
1174   XCBButtonPressEvent *theButtonPress;
1175   Bool  ContinueState = True;
1176   int error = 0;
1177   
1178   switch ( EventState ) {
1179   case NORMAL_STATE:
1180     while ( ContinueState &&
1181             NULL != (theEvent = XCBPollForEvent( xc, &error )) ) {  /*while ( XCheckMaskEvent( theDisplay, EVENT_MASK, &theEvent ) ) {*/
1182           switch ( theEvent->response_type & 0x7f ) {
1183           case XCBConfigureNotify:
1184             theConfigureNotification = (XCBConfigureNotifyEvent *)theEvent;
1185                 WindowWidth = theConfigureNotification->width;
1186                 WindowHeight = theConfigureNotification->height;
1187                 WindowPointX = theConfigureNotification->x;
1188                 WindowPointY = theConfigureNotification->y;
1189                 BorderWidth = theConfigureNotification->border_width;
1190                 NekoAdjust();
1191                 break;
1192           case XCBExpose:
1193             theExposure = (XCBExposeEvent *)theEvent;
1194                 if ( theExposure->count == 0 )
1195                   RedrawNeko();
1196                 break;
1197           case XCBMapNotify:
1198                 RedrawNeko();
1199                 break;
1200           case XCBKeyPress:
1201                 ContinueState = ProcessKeyPress( (XCBKeyPressEvent *)theEvent );
1202                 break;
1203           case XCBButtonPress:
1204             theButtonPress = (XCBButtonPressEvent *)theEvent;
1205                 ContinueState = ( theButtonPress->detail.id != 3 );     /* xbutton.button */
1206                 break;
1207           /* new: handle ClientMessage */
1208           case XCBClientMessage:
1209             ContinueState = !IsDeleteMessage((XCBClientMessageEvent *)theEvent);
1210             break;
1211           default:
1212                 /* Unknown Event */
1213                 /*printf("event type:%x\n", (int)theEvent->response_type);*/
1214                 break;
1215           }
1216           free(theEvent);
1217           if (error != 0)
1218                 return False;
1219         } /* end while */
1220         break;
1221 #ifdef  DEBUG
1222   case DEBUG_LIST:
1223         XNextEvent( theDisplay, &theEvent );
1224         switch ( theEvent.type ) {
1225         case ConfigureNotify:
1226           WindowWidth = theEvent.xconfigure.width;
1227           WindowHeight = theEvent.xconfigure.height;
1228           WindowPointX = theEvent.xconfigure.x;
1229           WindowPointY = theEvent.xconfigure.y;
1230           BorderWidth = theEvent.xconfigure.border_width;
1231           break;
1232         case Expose:
1233           if ( theEvent.xexpose.count == 0 )
1234                 DisplayCharacters();
1235           break;
1236         case MapNotify:
1237           DisplayCharacters();
1238           break;
1239         case KeyPress:
1240           ContinueState = ProcessKeyPress( &theEvent );
1241           break;
1242         case ButtonPress:
1243           if ( theEvent.xbutton.button == 3 )
1244                 return( False );
1245           break;
1246         default:
1247           /* Unknown Event */
1248           break;
1249         }
1250         break;
1251   case DEBUG_MOVE:
1252         while ( XCheckMaskEvent( theDisplay, EVENT_MASK, &theEvent ) ) {
1253           switch ( theEvent.type ) {
1254           case ConfigureNotify:
1255                 WindowWidth = theEvent.xconfigure.width;
1256                 WindowHeight = theEvent.xconfigure.height;
1257                 WindowPointX = theEvent.xconfigure.x;
1258                 WindowPointY = theEvent.xconfigure.y;
1259                 BorderWidth = theEvent.xconfigure.border_width;
1260                 NekoAdjust();
1261                 break;
1262           case Expose:
1263                 if ( theEvent.xexpose.count == 0 )
1264                   RedrawNeko();
1265                 break;
1266           case MapNotify:
1267                 RedrawNeko();
1268                 break;
1269           case KeyPress:
1270                 ContinueState = ProcessKeyPress( &theEvent );
1271                 if ( !ContinueState ) {
1272                   return( ContinueState );
1273                 }
1274                 break;
1275           case ButtonPress:
1276                 if ( theEvent.xbutton.button == 3 )
1277                   return( False );
1278                 break;
1279           default:
1280                 /* Unknown Event */
1281                 break;
1282           }
1283         }
1284         break;
1285 #endif
1286   default:
1287         /* Internal Error */
1288         break;
1289   }
1290   
1291   return( ContinueState );
1292 }
1293
1294
1295 void  ProcessNeko(void) {
1296   struct itimerval      Value;
1297   
1298   EventState = NORMAL_STATE;
1299
1300   NekoX = ( WindowWidth - BITMAP_WIDTH / 2 ) / 2;
1301   NekoY = ( WindowHeight - BITMAP_HEIGHT / 2 ) / 2;
1302   
1303   NekoLastX = NekoX;
1304   NekoLastY = NekoY;
1305   
1306   SetNekoState( NEKO_STOP );
1307   
1308   timerclear( &Value.it_interval );
1309   timerclear( &Value.it_value );
1310   
1311   Value.it_interval.tv_usec = IntervalTime;
1312   Value.it_value.tv_usec = IntervalTime;
1313   
1314   setitimer( ITIMER_REAL, &Value, 0 );
1315   
1316   do {
1317         NekoThinkDraw();
1318   } while ( ProcessEvent() );
1319 }
1320
1321 #ifdef  DEBUG
1322
1323 void  NekoList() {
1324   EventState = DEBUG_LIST;
1325   
1326   fprintf( stderr, "\n" );
1327   fprintf( stderr, "G-0lMw$rI=<($7$^$9!#(Quit !D Alt-Q)\n" );
1328   fprintf( stderr, "\n" );
1329   
1330   XSelectInput( theDisplay, theWindow, EVENT_MASK );
1331   
1332   while ( ProcessEvent() );
1333 }
1334
1335
1336 void  NekoMoveTest() {
1337   struct itimerval      Value;
1338   
1339   EventState = DEBUG_MOVE;
1340   
1341   NekoX = ( WindowWidth - BITMAP_WIDTH / 2 ) / 2;
1342   NekoY = ( WindowHeight - BITMAP_HEIGHT / 2 ) / 2;
1343   
1344   NekoLastX = NekoX;
1345   NekoLastY = NekoY;
1346   
1347   SetNekoState( NEKO_STOP );
1348   
1349   timerclear( &Value.it_interval );
1350   timerclear( &Value.it_value );
1351   
1352   Value.it_interval.tv_usec = IntervalTime;
1353   Value.it_value.tv_usec = IntervalTime;
1354   
1355   setitimer( ITIMER_REAL, &Value, 0 );
1356   
1357   fprintf( stderr, "\n" );
1358   fprintf( stderr, "G-$N0\F0%F%9%H$r9T$$$^$9!#(Quit !D Alt-Q)\n" );
1359   fprintf( stderr, "\n" );
1360   fprintf( stderr, "\t%-!<%Q%C%I>e$N%F%s%-!<$GG-$r0\F0$5$;$F2<$5$$!#\n" );
1361   fprintf( stderr, "\t(M-8z$J%-!<$O#1!A#9$G$9!#)\n" );
1362   fprintf( stderr, "\n" );
1363   
1364   do {
1365         NekoThinkDraw();
1366   } while ( ProcessEvent() );
1367 }
1368
1369
1370 void  ProcessDebugMenu() {
1371   int           UserSelectNo = 0;
1372   char  UserAnswer[ BUFSIZ ];
1373   
1374   fprintf( stderr, "\n" );
1375   fprintf( stderr, "!Zxneko %G%P%C%0%a%K%e!<![\n" );
1376   
1377   while ( !( UserSelectNo >= 1 && UserSelectNo <= 2 ) ) {
1378         fprintf( stderr, "\n" );
1379         fprintf( stderr, "\t1)!!G-%-%c%i%/%?!<0lMwI=<(\n" );
1380         fprintf( stderr, "\t2)!!G-0\F0%F%9%H\n" );
1381         fprintf( stderr, "\n" );
1382         fprintf( stderr, "Select: " );
1383
1384         fgets( UserAnswer, sizeof( UserAnswer ), stdin );
1385         
1386         UserSelectNo = atoi( UserAnswer );
1387         
1388         if ( !( UserSelectNo >= 1 && UserSelectNo <= 2 ) ) {
1389           fprintf( stderr, "\n" );
1390           fprintf( stderr, "@5$7$$HV9f$rA*Br$7$F2<$5$$!#\n" );
1391         }
1392   }
1393   
1394   switch ( UserSelectNo ) {
1395   case 1:
1396         NekoList();
1397         break;
1398   case 2:
1399         NekoMoveTest();
1400         break;
1401   default:
1402         /* Internal Error */
1403         break;
1404   }
1405   
1406   fprintf( stderr, "%F%9%H=*N;!#\n" );
1407   fprintf( stderr, "\n" );
1408 }
1409
1410 #endif  /* DEBUG */
1411
1412 void  NullFunction(int ignored)
1413 {
1414   /* signal( SIGALRM, NullFunction ); */
1415 }
1416
1417
1418 void  Usage(void) {
1419   fprintf( stderr,
1420                   "Usage: %s [-display <display>] [-geometry <geometry>] \\\n",
1421                   ProgramName );
1422   fprintf( stderr, "\t[-bg <background>] [-fg <foreground>] \\\n" );
1423   fprintf( stderr, "\t[-title <title>] [-name <title>] [-iconic] \\\n" );
1424   fprintf( stderr, "\t[-speed <speed>] [-time <time>] [-root] [-help]\n" );
1425 }
1426
1427
1428 Bool
1429 GetArguments( int argc, char *argv[],
1430         char *theDisplayName, char *theGeometry, char **theTitle,
1431         double *NekoSpeed, long *IntervalTime )
1432 {
1433   int           ArgCounter;
1434   Bool  iconicState;
1435   
1436   theDisplayName[ 0 ] = '\0';
1437   theGeometry[ 0 ] = '\0';
1438   
1439   iconicState = False;
1440   
1441   for ( ArgCounter = 0; ArgCounter < argc; ArgCounter++ ) {
1442         if ( strncmp( argv[ ArgCounter ], "-h", 2 ) == 0 ) {
1443           Usage();
1444           exit( 0 );
1445         } else if ( strcmp( argv[ ArgCounter ], "-display" ) == 0 ) {
1446           ArgCounter++;
1447           if ( ArgCounter < argc ) {
1448                 strcpy( theDisplayName, argv[ ArgCounter ] );
1449           } else {
1450                 fprintf( stderr, "%s: -display option error.\n", ProgramName );
1451                 exit( 1 );
1452           }
1453         } else if ( strncmp( argv[ ArgCounter ], "-geom", 5 ) == 0 ) {
1454           ArgCounter++;
1455           if ( ArgCounter < argc ) {
1456                 strcpy( theGeometry, argv[ ArgCounter ] );
1457           } else {
1458                 fprintf( stderr,
1459                                 "%s: -geometry option error.\n", ProgramName );
1460                 exit( 1 );
1461           }
1462         } else if ( ( strcmp( argv[ ArgCounter ], "-title" ) == 0 )
1463                            || ( strcmp( argv[ ArgCounter ], "-name" ) == 0 ) ) {
1464           ArgCounter++;
1465           if ( ArgCounter < argc ) {
1466             *theTitle = argv[ ArgCounter ];
1467           } else {
1468                 fprintf( stderr, "%s: -title option error.\n", ProgramName );
1469                 exit( 1 );
1470           }
1471         } else if ( strcmp( argv[ ArgCounter ], "-iconic" ) == 0 ) {
1472           iconicState = True;
1473         } else if ( strcmp( argv[ ArgCounter ], "-speed" ) == 0 ) {
1474           ArgCounter++;
1475           if ( ArgCounter < argc ) {
1476                 *NekoSpeed = atof( argv[ ArgCounter ] );
1477           } else {
1478                 fprintf( stderr, "%s: -speed option error.\n", ProgramName );
1479                 exit( 1 );
1480           }
1481         } else if ( strcmp( argv[ ArgCounter ], "-fg" ) == 0 ) {
1482           ArgCounter++;
1483           if ( ArgCounter < argc ) {
1484                 fgColor = argv[ArgCounter];
1485           } else {
1486                 fprintf( stderr, "%s: -fg option error.\n", ProgramName );
1487                 exit( 1 );
1488           }
1489         } else if ( strcmp( argv[ ArgCounter ], "-bg" ) == 0 ) {
1490           ArgCounter++;
1491           if ( ArgCounter < argc ) {
1492                 bgColor = argv[ArgCounter];
1493           } else {
1494                 fprintf( stderr, "%s: -bg option error.\n", ProgramName );
1495                 exit( 1 );
1496           }
1497         } else if ( strcmp( argv[ ArgCounter ], "-time" ) == 0 ) {
1498           ArgCounter++;
1499           if ( ArgCounter < argc ) {
1500                 *IntervalTime = atol( argv[ ArgCounter ] );
1501           } else {
1502                 fprintf( stderr, "%s: -time option error.\n", ProgramName );
1503                 exit( 1 );
1504           }
1505         } else if ( strcmp( argv[ ArgCounter ], "-root" ) == 0 ) {
1506           useRoot = 1;
1507         } else {
1508           fprintf( stderr,
1509                           "%s: Unknown option \"%s\".\n", ProgramName,
1510                           argv[ ArgCounter ] );
1511           Usage();
1512           exit( 1 );
1513         }
1514   }
1515   
1516   return( iconicState );
1517 }
1518
1519 void UndefineCursor( XCBConnection *c, XCBWINDOW w)
1520 {
1521         CARD32 none[] = { XCBNone };
1522         XCBChangeWindowAttributes( c, w, XCBCWCursor, none );
1523 }
1524
1525 int
1526 main( int argc, char *argv[] )
1527 {
1528   Bool  iconicState;
1529   char  theDisplayName[ DIRNAMELEN ];
1530   char  theGeometry[ DIRNAMELEN ];
1531   char  *theTitle = "";
1532   
1533   ProgramName = argv[ 0 ];
1534   
1535   argc--;
1536   argv++;
1537   
1538   useRoot = 0;
1539   fgColor = bgColor = (char *) NULL;
1540   
1541   iconicState = GetArguments( argc, argv, theDisplayName, theGeometry,
1542                                                          &theTitle, &NekoSpeed, &IntervalTime );
1543   
1544   if (theTitle[0] == 0) theTitle = ProgramName;
1545   InitScreen( theDisplayName, theGeometry, theTitle, iconicState );
1546   
1547   signal( SIGALRM, NullFunction );
1548   
1549   SinPiPer8Times3 = sin( PI_PER8 * (double)3 );
1550   SinPiPer8 = sin( PI_PER8 );
1551
1552 #ifndef DEBUG
1553   ProcessNeko();
1554 #else
1555   ProcessDebugMenu();
1556 #endif
1557
1558   UndefineCursor( xc, theWindow );
1559   XCBDisconnect( xc );
1560   exit( 0 );
1561 }