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