X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fpong.c;h=fe200c35a65620d7c46e63f756dc4c44a615096f;hp=a68872865b44e3765ca6c2bff15ef7577ae98229;hb=aa75c7476aeaa84cf3abc192b376a8b03c325213;hpb=88cfe534a698a0562e81345957a50714af1453bc diff --git a/hacks/pong.c b/hacks/pong.c index a6887286..fe200c35 100644 --- a/hacks/pong.c +++ b/hacks/pong.c @@ -22,6 +22,11 @@ * not displaying seconds, who cares. While I was at it, I added a -noise option * to control the noisyness of the display. * + * Modified by Dave Odell to add -p1 and -p2 options. + * JWXYZ doesn't support XWarpPointer, so PLAYER_MOUSE only works on native + * X11. JWXYZ also doesn't support cursors, so PLAYER_TABLET doesn't hide the + * mouse pointer. + * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that @@ -52,9 +57,22 @@ #include "screenhack.h" #include "analogtv.h" +#include +#ifndef HAVE_JWXYZ +# include +#endif /* #define OUTPUT_POS */ +typedef enum { + PLAYER_AI, + PLAYER_MOUSE, + PLAYER_TABLET, + PLAYER_KEYBOARD, + PLAYER_KEYBOARD_LEFT +} PlayerType; + typedef struct _paddle { + PlayerType player; int x; int y; int w; @@ -96,6 +114,18 @@ struct state { int net_ntsc[4]; analogtv_font score_font; + +# ifndef HAVE_JWXYZ + Cursor null_cursor; +# endif + int mouse_y; + unsigned w, h, screen_h, screen_h_mm; + Bool is_focused; + Bool key_w: 1; + Bool key_s: 1; + Bool key_up: 1; + Bool key_down: 1; + unsigned int dragging : 2; }; @@ -250,12 +280,83 @@ hit_paddle(struct state *st) } } +static PlayerType +get_player_type(Display *dpy, char *rsrc) +{ + PlayerType result; + char *s = get_string_resource(dpy, rsrc, "String"); + if (!strcmp(s, "ai") || !strcmp(s, "AI")) + { + result = PLAYER_AI; + } +# ifndef HAVE_JWXYZ + else if (!strcmp(s, "mouse")) + { + result = PLAYER_MOUSE; + } +# endif + else if (!strcmp(s, "tab") || !strcmp(s, "tablet")) + { + result = PLAYER_TABLET; + } + else if (!strcmp(s, "kb") || !strcmp(s, "keyb") || !strcmp(s, "keyboard") || + !strcmp(s, "right") || !strcmp(s, "kbright") || + !strcmp(s, "arrows")) + { + result = PLAYER_KEYBOARD; + } + else if (!strcmp(s, "left") || !strcmp(s, "kbleft") || + !strcmp(s, "ws") || !strcmp(s, "wasd")) + { + result = PLAYER_KEYBOARD_LEFT; + } + else + { + fprintf(stderr, "%s: invalid player type\n", progname); + result = PLAYER_AI; + } + free(s); + return result; +} + +static void +do_shape (struct state *st, const XWindowAttributes *xgwa) +{ + st->w = xgwa->width; + st->h = xgwa->height; + st->screen_h = XHeightOfScreen(xgwa->screen); + st->screen_h_mm = XHeightMMOfScreen(xgwa->screen); +} + +#ifndef HAVE_JWXYZ +static Bool +needs_grab (struct state *st) +{ + return + st->l_paddle.player == PLAYER_MOUSE || + st->r_paddle.player == PLAYER_MOUSE; +/* + st->l_paddle.player == PLAYER_TABLET || + st->r_paddle.player == PLAYER_TABLET; + */ +} + +static void +grab_pointer (struct state *st) +{ + st->is_focused = True; + XGrabPointer(st->dpy, st->window, True, PointerMotionMask, GrabModeAsync, + GrabModeAsync, st->window, st->null_cursor, CurrentTime); +} +#endif /* !HAVE_JWXYZ */ + static void * pong_init (Display *dpy, Window window) { struct state *st = (struct state *) calloc (1, sizeof(*st)); int i; + XWindowAttributes xgwa; struct { int w, h; char *s[10]; @@ -500,6 +601,7 @@ pong_init (Display *dpy, Window window) #endif /*Init the paddles*/ + st->l_paddle.player = get_player_type(dpy, "p1"); st->l_paddle.x = 8; st->l_paddle.y = 100; st->l_paddle.w = 16; @@ -507,6 +609,7 @@ pong_init (Display *dpy, Window window) st->l_paddle.wait = 1; st->l_paddle.lock = 0; st->r_paddle = st->l_paddle; + st->r_paddle.player = get_player_type(dpy, "p2"); st->r_paddle.x = PONG_W - 8 - st->r_paddle.w; st->r_paddle.wait = 0; /*Init the ball*/ @@ -515,6 +618,59 @@ pong_init (Display *dpy, Window window) st->ball.w = 16; st->ball.h = 8; + /* The mouse warping business breaks tablet input. */ + if (st->l_paddle.player == PLAYER_MOUSE && + st->r_paddle.player == PLAYER_TABLET) + { + st->l_paddle.player = PLAYER_TABLET; + } + if (st->r_paddle.player == PLAYER_MOUSE && + st->l_paddle.player == PLAYER_TABLET) + { + st->r_paddle.player = PLAYER_TABLET; + } + + if (st->clock) { + st->l_paddle.player = PLAYER_AI; + st->r_paddle.player = PLAYER_AI; + fprintf(stderr, "%s: clock mode requires AI control\n", progname); + + } + +# ifndef HAVE_JWXYZ + if (st->l_paddle.player == PLAYER_MOUSE || + st->r_paddle.player == PLAYER_MOUSE || + st->l_paddle.player == PLAYER_TABLET || + st->r_paddle.player == PLAYER_TABLET) + { + XColor black = {0, 0, 0, 0}; + Pixmap cursor_pix = XCreatePixmap(dpy, window, 4, 4, 1); + XGCValues gcv; + GC mono_gc; + + gcv.foreground = 0; + mono_gc = XCreateGC(dpy, cursor_pix, GCForeground, &gcv); + st->null_cursor = XCreatePixmapCursor(dpy, cursor_pix, cursor_pix, + &black, &black, 0, 0); + XFillRectangle(dpy, cursor_pix, mono_gc, 0, 0, 4, 4); + XFreeGC(dpy, mono_gc); + + XSelectInput(dpy, window, + PointerMotionMask | FocusChangeMask | + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask); + + if (needs_grab(st)) + { + grab_pointer(st); + } + else + { + XDefineCursor(dpy, window, st->null_cursor); + } + } +# endif + st->m_unit = get_integer_resource (st->dpy, "speed", "Integer"); st->noise = get_float_resource(st->dpy, "noise", "Float"); st->clock = get_boolean_resource(st->dpy, "clock", "Boolean"); @@ -540,41 +696,94 @@ pong_init (Display *dpy, Window window) ANALOGTV_TOP, ANALOGTV_BOT, st->field_ntsc); + XGetWindowAttributes(dpy, window, &xgwa); + do_shape(st, &xgwa); + return st; } static void p_logic(struct state *st, Paddle *p) { - int targ; - if (st->bx > 0) { - targ = st->ball.y + st->by * (st->r_paddle.x-st->ball.x) / st->bx; - } - else if (st->bx < 0) { - targ = st->ball.y - st->by * (st->ball.x - st->l_paddle.x - st->l_paddle.w) / st->bx; + if (p->player == PLAYER_AI) + { + if (!p->wait) + { + int targ; + if (st->bx > 0) { + targ = st->ball.y + st->by * (st->r_paddle.x-st->ball.x) / st->bx; + } + else if (st->bx < 0) { + targ = st->ball.y - st->by * (st->ball.x - st->l_paddle.x - st->l_paddle.w) / st->bx; + } + else { + targ = st->ball.y; + } + if (targ > PONG_H) targ=PONG_H; + if (targ < 0) targ=0; + + if (targ < p->y && !p->lock) + { + p->y -= st->paddle_rate; + } + else if (targ > (p->y + p->h) && !p->lock) + { + p->y += st->paddle_rate; + } + else + { + int move=targ - (p->y + p->h/2); + if (move>st->paddle_rate) move=st->paddle_rate; + if (move<-st->paddle_rate) move=-st->paddle_rate; + p->y += move; + p->lock = 1; + } + } } - else { - targ = st->ball.y; +# ifndef HAVE_JWXYZ + else if (p->player == PLAYER_MOUSE) + { + /* Clipping happens elsewhere. */ + /* As the screen resolution increases, the mouse moves faster in terms of + pixels, so divide by DPI. */ + p->y += (int)(st->mouse_y - (st->h / 2)) * 4 * (int)st->screen_h_mm / (3 * (int)st->screen_h); + if (st->is_focused) + XWarpPointer (st->dpy, None, st->window, 0, 0, 0, 0, st->w / 2, st->h / 2); } - if (targ > PONG_H) targ=PONG_H; - if (targ < 0) targ=0; - - if (targ < p->y && !p->lock) +# endif + else if (p->player == PLAYER_TABLET) { - p->y -= st->paddle_rate; + p->y = st->mouse_y * (PONG_H - PONG_TMARG) / st->h + PONG_TMARG - p->h / 2; } - else if (targ > (p->y + p->h) && !p->lock) + else if (p->player == PLAYER_KEYBOARD) { - p->y += st->paddle_rate; + if (st->key_up) + p->y -= 8; + if (st->key_down) + p->y += 8; } - else + else if (p->player == PLAYER_KEYBOARD_LEFT) { - int move=targ - (p->y + p->h/2); - if (move>st->paddle_rate) move=st->paddle_rate; - if (move<-st->paddle_rate) move=-st->paddle_rate; - p->y += move; - p->lock = 1; + if (st->key_w) + p->y -= 8; + if (st->key_s) + p->y += 8; } + + if ((st->dragging == 1 && p == &st->l_paddle) || + (st->dragging == 2 && p == &st->r_paddle)) + { + /* Not getting MotionNotify. */ + Window root1, child1; + int mouse_x, mouse_y, root_x, root_y; + unsigned int mask; + if (XQueryPointer (st->dpy, st->window, &root1, &child1, + &root_x, &root_y, &mouse_x, &mouse_y, &mask)) + st->mouse_y = mouse_y; + + if (st->mouse_y < 0) st->mouse_y = 0; + p->y = st->mouse_y * (PONG_H - PONG_TMARG) / st->h + PONG_TMARG - p->h / 2; + } } static void @@ -681,11 +890,26 @@ paint_net(struct state *st) } } +static double +double_time (void) +{ + struct timeval now; +# ifdef GETTIMEOFDAY_TWO_ARGS + struct timezone tzp; + gettimeofday(&now, &tzp); +# else + gettimeofday(&now); +# endif + + return (now.tv_sec + ((double) now.tv_usec * 0.000001)); +} + static unsigned long pong_draw (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; const analogtv_reception *reception = &st->reception; + double then = double_time(), now, timedelta; if (st->clock) { @@ -716,14 +940,8 @@ pong_draw (Display *dpy, Window window, void *closure) } } - if (!st->r_paddle.wait) - { - p_logic(st, &st->r_paddle); - } - if (!st->l_paddle.wait) - { - p_logic(st, &st->l_paddle); - } + p_logic(st, &st->r_paddle); + p_logic(st, &st->l_paddle); p_hit_top_bottom(&st->r_paddle); p_hit_top_bottom(&st->l_paddle); @@ -748,11 +966,9 @@ pong_draw (Display *dpy, Window window, void *closure) analogtv_reception_update(&st->reception); analogtv_draw(st->tv, st->noise, &reception, 1); -#ifdef USE_IPHONE - return 0; -#else - return 5000; -#endif + now = double_time(); + timedelta = (1 / 29.97) - (now - then); + return timedelta > 0 ? timedelta * 1000000 : 0; } @@ -763,6 +979,8 @@ static const char *pong_defaults [] = { "*speed: 6", "*noise: 0.04", "*clock: false", + "*p1: ai", + "*p2: ai", ANALOGTV_DEFAULTS 0 }; @@ -771,6 +989,8 @@ static XrmOptionDescRec pong_options [] = { { "-speed", ".speed", XrmoptionSepArg, 0 }, { "-noise", ".noise", XrmoptionSepArg, 0 }, { "-clock", ".clock", XrmoptionNoArg, "true" }, + { "-p1", ".p1", XrmoptionSepArg, 0 }, + { "-p2", ".p2", XrmoptionSepArg, 0 }, ANALOGTV_OPTIONS { 0, 0, 0, 0 } }; @@ -780,12 +1000,110 @@ pong_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { struct state *st = (struct state *) closure; + XWindowAttributes xgwa; analogtv_reconfigure (st->tv); + + XGetWindowAttributes(dpy, window, &xgwa); /* AnalogTV does this too. */ + xgwa.width = w; + xgwa.height = h; + do_shape(st, &xgwa); } static Bool pong_event (Display *dpy, Window window, void *closure, XEvent *event) { + struct state *st = (struct state *) closure; + switch (event->type) + { + case MotionNotify: + st->mouse_y = event->xmotion.y; + break; +# ifndef HAVE_JWXYZ + case FocusIn: + if (needs_grab(st)) + { + grab_pointer(st); + } + break; + case FocusOut: + if (needs_grab(st)) + { + XUngrabPointer (dpy, CurrentTime); + st->is_focused = False; + } + break; +# endif /* !HAVE_JWXYZ */ + case KeyPress: + case KeyRelease: + { + char c; + KeySym key; + XLookupString(&event->xkey, &c, 1, &key, 0); + Bool is_pressed = event->type == KeyPress; + switch(key) + { + case XK_Up: + if (st->l_paddle.player == PLAYER_KEYBOARD || + st->r_paddle.player == PLAYER_KEYBOARD) + { + st->key_up = is_pressed; + return True; + } + break; + case XK_Down: + if (st->l_paddle.player == PLAYER_KEYBOARD || + st->r_paddle.player == PLAYER_KEYBOARD) + { + st->key_down = is_pressed; + return True; + } + break; + case 'w': + if (st->l_paddle.player == PLAYER_KEYBOARD_LEFT || + st->r_paddle.player == PLAYER_KEYBOARD_LEFT) + { + st->key_w = is_pressed; + return True; + } + break; + case 's': + if (st->l_paddle.player == PLAYER_KEYBOARD_LEFT || + st->r_paddle.player == PLAYER_KEYBOARD_LEFT) + { + st->key_s = is_pressed; + return True; + } + break; + } + } + + /* Allow the user to pick up and drag either paddle with the mouse, + even when not in a mouse-paddle mode. */ + + case ButtonPress: + if (st->dragging != 0) + return False; + else if (event->xbutton.x < st->w * 0.2) + { + if (st->l_paddle.player != PLAYER_MOUSE) + st->dragging = 1; + return True; + } + else if (event->xbutton.x > st->w * 0.8) + { + if (st->r_paddle.player != PLAYER_MOUSE) + st->dragging = 2; + return True; + } + break; + case ButtonRelease: + if (st->dragging != 0) + { + st->dragging = 0; + return True; + } + break; + } return False; }