/* xscreensaver, Copyright (c) 2022 YOURNAME * * 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 * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #include #include "screenhack.h" #include "alpha.h" #ifdef HAVE_DOUBLE_BUFFER_EXTENSION #include "xdbe.h" #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ #define PI 3.141592 /* degree to radian */ static double deg2rad(double angle) { return (angle * PI / 180.0); } /* screensaver state */ struct state { Display *dpy; Window window; int nplanes; unsigned long base_pixel; unsigned long *plane_masks; Bool monochrome; Bool use_dbuf; int delay; int count; int speed; struct ball **objs; XWindowAttributes xgwa; int scrw, scrh; XColor color; GC erase_gc; Pixmap b, ba, bb; /* double-buffer to reduce flicker */ #ifdef HAVE_DOUBLE_BUFFER_EXTENSION Bool dbeclear_p; XdbeBackBuffer backb; #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ }; /* ball object */ struct ball { double x, y; double dx, dy; int w, h; int scrw, scrh; int fuse; GC gc; }; /* create ball */ static struct ball * create_ball(struct state *st, Drawable d, int scrw, int scrh, unsigned long pixel) { struct ball *t = (struct ball *)malloc(sizeof(*t)); t->scrw = scrw; t->scrh = scrh; t->w = t->h = ((scrw > scrh)? scrh : scrw) / 16; /* set position */ t->x = (random() % (t->scrw / 2)) + (t->scrw / 4); t->y = (random() % (t->scrh / 2)) + (t->scrh / 4); /* set direction */ { double rad; rad = deg2rad(random() % 360); t->dx = st->speed * cos(rad); t->dy = st->speed * sin(rad); } t->fuse = 1 + (random() % 4); { XGCValues gcv; unsigned long flags; flags = GCForeground; gcv.foreground = pixel; gcv.line_width = 3; gcv.cap_style = CapProjecting; gcv.join_style = JoinMiter; flags |= (GCLineWidth | GCCapStyle | GCJoinStyle); t->gc = XCreateGC(st->dpy, d, flags, &gcv); } return t; } /* ball move and draw */ static int ball_exec(struct state *st, Drawable window, struct ball *t) { /* draw circle */ XDrawArc(st->dpy, window, t->gc, t->x, t->y, t->w, t->h, 0, 360 * 64); /* XFillArc(st->dpy, window, t->gc, t->x, t->y, t->w, t->h, 0, 360 * 64); */ /* move */ t->x += t->dx; t->y += t->dy; /* change directon */ if (t->x <= 0 || (t->x + t->w) >= t->scrw) t->dx *= -1; if (t->y <= 0 || (t->y + t->h) >= t->scrh) t->dy *= -1; if (t->fuse <= 0) { /* I die. */ XFreeGC(st->dpy, t->gc); memset(t, 0, sizeof(*t)); free(t); return -1; } return 0; } /* * init screensaver. * Return an object holding your global state. */ static void * helloxsaver2_init(Display *dpy, Window window) { struct state *st = (struct state *)calloc(1, sizeof(*st)); XGCValues gcv; int i; st->dpy = dpy; st->window = window; /* get parameter */ st->delay = get_integer_resource(st->dpy, "delay", "Integer"); st->count = get_integer_resource(st->dpy, "count", "Integer"); /* st->speed = get_integer_resource(st->dpy, "speed", "Integer"); */ st->speed = get_integer_resource(st->dpy, "speed", "Speed"); st->use_dbuf = get_boolean_resource(st->dpy, "doubleBuffer", "Boolean"); st->monochrome = get_boolean_resource(st->dpy, "mono", "Boolean"); #ifdef HAVE_DOUBLE_BUFFER_EXTENSION st->dbeclear_p = get_boolean_resource(st->dpy, "useDBEClear", "Boolean"); #endif #ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */ st->use_dbuf = False; #endif /* get window attributes */ XGetWindowAttributes(st->dpy, st->window, &st->xgwa); st->scrw = st->xgwa.width; st->scrh = st->xgwa.height; /* define colors */ st->color.pixel = get_pixel_resource(st->dpy, st->xgwa.colormap, "foreground", "Foreground"); if (st->use_dbuf) { #ifdef HAVE_DOUBLE_BUFFER_EXTENSION if (st->dbeclear_p) st->b = xdbe_get_backbuffer(st->dpy, st->window, XdbeBackground); else st->b = xdbe_get_backbuffer(st->dpy, st->window, XdbeUndefined); st->backb = st->b; #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ if (!st->b) { st->ba = XCreatePixmap(st->dpy, st->window, st->scrw, st->scrh, st->xgwa.depth); st->bb = XCreatePixmap(st->dpy, st->window, st->scrw, st->scrh, st->xgwa.depth); st->b = st->ba; } } else { st->b = st->window; } gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap, "background", "Background"); st->erase_gc = XCreateGC(st->dpy, st->b, GCForeground, &gcv); if (st->ba) XFillRectangle(st->dpy, st->ba, st->erase_gc, 0, 0, st->scrw, st->scrh); if (st->bb) XFillRectangle(st->dpy, st->bb, st->erase_gc, 0, 0, st->scrw, st->scrh); /* malloc ball objects */ st->objs = (struct ball **)calloc(st->count, sizeof(struct ball *)); for (i = 0; i < st->count; i++) st->objs[i] = create_ball(st, st->b, st->scrw, st->scrh, st->color.pixel); return st; } /* Draw a single frame */ static unsigned long helloxsaver2_draw(Display *dpy, Window window, void *closure) { struct state *st = (struct state *)closure; /* clear double buffer */ #ifdef HAVE_DOUBLE_BUFFER_EXTENSION if (!st->dbeclear_p || !st->backb) XFillRectangle(st->dpy, st->b, st->erase_gc, 0, 0, st->scrw, st->scrh); #else XFillRectangle(st->dpy, st->b, st->erase_gc, 0, 0, st->scrw, st->scrh); #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ /* balls move and draw */ { int i; for (i = 0; i < st->count; i++) { if (ball_exec(st, st->b, st->objs[i]) < 0) { /* The ball is already dead. */ st->objs[i] = create_ball(st, st->b, st->scrw, st->scrh, st->color.pixel); } } } /* draw double buffer */ #ifdef HAVE_DOUBLE_BUFFER_EXTENSION if (st->backb) { XdbeSwapInfo info[1]; info[0].swap_window = st->window; info[0].swap_action = (st->dbeclear_p ? XdbeBackground : XdbeUndefined); XdbeSwapBuffers(st->dpy, info, 1); } else #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ if (st->use_dbuf) { XCopyArea(st->dpy, st->b, st->window, st->erase_gc, 0, 0, st->scrw, st->scrh, 0, 0); st->b = (st->b == st->ba ? st->bb : st->ba); } return st->delay; } /* Called when the window is resized. */ static void helloxsaver2_reshape(Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { struct state *st = (struct state *)closure; if (!st->use_dbuf) { /* #### more complicated if we have a back buffer... */ int i; XGetWindowAttributes(st->dpy, st->window, &st->xgwa); st->scrw = st->xgwa.width; st->scrh = st->xgwa.height; XClearWindow(dpy, window); for (i = 0; i < st->count; i++) { if (st->objs[i]) st->objs[i]->fuse = 0; } } } /* Called when a keyboard or mouse event happens. */ static Bool helloxsaver2_event(Display *dpy, Window window, void *closure, XEvent *event) { return False; } /* Free everything you've allocated. */ static void helloxsaver2_free(Display *dpy, Window window, void *closure) { struct state *st = (struct state *)closure; int i; XFreeGC(dpy, st->erase_gc); if (st->ba) XFreePixmap(dpy, st->ba); if (st->bb) XFreePixmap(dpy, st->bb); if (st->plane_masks) free(st->plane_masks); for (i = 0; i < st->count; i++) if (st->objs[i]) { XFreeGC(dpy, st->objs[i]->gc); free(st->objs[i]); } free(st->objs); free(st); } /* Default values for the resources you use. */ static const char *helloxsaver2_defaults[] = { ".background: black", ".foreground: white", "*delay: 10000", "*count: 4", "*speed: 15", "*doubleBuffer: True", #ifdef HAVE_DOUBLE_BUFFER_EXTENSION "*useDBE: True", "*useDBEClear: True", #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ #ifdef HAVE_MOBILE "*ignoreRotation: True", #endif 0}; /* The command-line options you accept. */ static XrmOptionDescRec helloxsaver2_options[] = { {"-delay", ".delay", XrmoptionSepArg, 0}, {"-count", ".count", XrmoptionSepArg, 0}, {"-speed", ".speed", XrmoptionSepArg, 0}, {"-db", ".doubleBuffer", XrmoptionNoArg, "True"}, {"-no-db", ".doubleBuffer", XrmoptionNoArg, "False"}, {0, 0, 0, 0}}; /* * The last line of the file should be * XSCREENSAVER_MODULE ("YourSaverName", yoursavername) */ XSCREENSAVER_MODULE("Helloxsaver2", helloxsaver2)