/* 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. */ /* fork noseguy.c, deluxe.c */ #include #include #include "screenhack.h" #include "ximage-loader.h" #ifdef HAVE_DOUBLE_BUFFER_EXTENSION #include "xdbe.h" #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ /* include png image binary */ #include "images/gen/scifi_bg_chip2_png.h" #define IMG_DATA scifi_bg_chip2_png #define IMG_W 8 #define IMG_H 10 #define OUT_W 640 #define OUT_H 360 #define OBJ_MAX 16 #define BG_COUNT 3 #define BG_W 16 #define BG_H 6 #define BG_CHIP_W 64 #define BG_CHIP_H 64 /* tilemap index data */ static int bg_layer[BG_COUNT][BG_W * BG_H] = { {43, 44, 29, 30, 43, 44, 27, 28, 43, 44, 29, 30, 43, 44, 27, 28, 51, 52, 37, 38, 51, 52, 35, 36, 51, 52, 37, 38, 51, 52, 35, 36, 27, 28, 43, 44, 27, 28, 29, 31, 51, 52, 43, 44, 27, 28, 29, 30, 35, 36, 51, 52, 35, 36, 37, 37, 37, 38, 51, 52, 35, 36, 37, 38, 29, 30, 27, 28, 43, 44, 27, 28, 43, 44, 27, 28, 29, 30, 27, 28, 37, 38, 35, 36, 51, 52, 35, 36, 51, 52, 35, 36, 37, 38, 35, 36}, {0, 24, 41, 48, 48, 26, 0, 24, 48, 48, 26, 40, 48, 48, 48, 26, 0, 49, 0, 0, 0, 40, 48, 41, 26, 0, 32, 48, 48, 26, 0, 49, 48, 41, 48, 25, 48, 25, 48, 48, 41, 48, 33, 48, 48, 33, 48, 41, 0, 0, 0, 32, 48, 33, 48, 48, 26, 0, 40, 48, 26, 40, 26, 0, 48, 48, 25, 41, 48, 41, 48, 25, 41, 48, 25, 25, 41, 48, 41, 48, 0, 0, 49, 0, 0, 0, 0, 40, 48, 48, 42, 49, 0, 0, 0, 0}, {1, 2, 3, 14, 15, 0, 6, 1, 1, 2, 3, 1, 1, 7, 0, 6, 12, 13, 7, 0, 0, 0, 0, 12, 13, 3, 2, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 5, 0, 0, 0, 8, 9, 3, 2, 10, 11, 0, 0, 4, 1, 1, 1, 2, 3, 2, 3, 2, 3, 1, 3, 1, 3, 2, 3, 2, 3}}; /* obj work */ typedef struct { Bool enable; int base_pix_id; int add_pix_id; float x, y; float dx, dy; int w, h; int counter; } Obj; /* Pixmap pix and mask */ typedef struct { Pixmap pix; Pixmap mask; } Pixmaps; /* screensaver state */ struct state { Display *dpy; Window win; Pixmap backbuf, ba, bb; /* double-buffer to reduce flicker */ Bool use_dbuf; Bool monochrome; #ifdef HAVE_DOUBLE_BUFFER_EXTENSION Bool dbeclear_p; XdbeBackBuffer backb; #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ XWindowAttributes xgwa; int scrw, scrh; XGCValues gcv; Colormap cmap; int delay; int speed; Pixmaps image0; int img_w, img_h; Pixmaps *pixs; int pixs_size; Pixmap smallbuf; Pixmap largebuf; XColor fgcolor, bgcolor; unsigned long fg, bg; GC gc; GC erase_gc; float bg_x[BG_COUNT]; float bg_y[BG_COUNT]; float bg_dx[BG_COUNT]; float bg_dy[BG_COUNT]; Obj objs[OBJ_MAX]; int enemy_born_timer; }; /* draw transparent image */ static void draw_image(Display *dpy, Drawable dst, GC gc, Pixmaps *src, int x, int y, int w, int h) { XSetClipMask(dpy, gc, src->mask); XSetClipOrigin(dpy, gc, x, y); XCopyArea(dpy, src->pix, dst, gc, 0, 0, w, h, x, y); XSetClipMask(dpy, gc, None); } /* get a color value from a color name */ /* static unsigned long get_color(Display *dpy, Colormap cmap, char *colorname) { XColor near_color, true_color; XAllocNamedColor(dpy, cmap, colorname, &near_color, &true_color); return near_color.pixel; } */ static void init_objs(struct state *st) { int i; for (i = 0; i < OBJ_MAX; i++) st->objs[i].enable = False; st->enemy_born_timer = 0; } static void create_obj(struct state *st, Obj *o, int x, int y, int pix_id) { o->enable = True; o->x = x; o->y = y; o->w = 64; o->h = 64; o->dx = -2.5; o->dy = 0; o->base_pix_id = 8 * 9; o->add_pix_id = pix_id % 8; o->counter = 0; } static void update_obj(struct state *st, Obj *o) { if (!o->enable) return; /* move */ o->x += o->dx; o->y += o->dy; /* update anime pattern */ o->counter++; if (o->counter >= 3) { o->counter = 0; o->add_pix_id = ((o->add_pix_id + 1) % 8); } if (o->x < -(o->w / 2) || o->y < -(o->h / 2) || o->y > OUT_H + (o->h / 2)) { o->enable = False; } } static int get_empty_obj_work(struct state *st) { int i; for (i = 0; i < OBJ_MAX; i++) if (!st->objs[i].enable) return i; return -1; } static void update_objs(struct state *st) { int i; /* born enemy */ st->enemy_born_timer--; if (st->enemy_born_timer <= 0) { int y = random() % (OUT_H - 36 * 2) + 36; int pix_id = 0; int cnt = random() % 3 + 3; st->enemy_born_timer = 110 + random() % 90; for (i = 0; i < cnt; i++) { int idx = get_empty_obj_work(st); if (idx < 0) break; /* not found empty work */ { int x = OUT_W + 64 + (64 * 1.00) * i; create_obj(st, &st->objs[idx], x, y, pix_id); pix_id = (pix_id + 6) % 8; } } } /* move objs */ for (i = 0; i < OBJ_MAX; i++) if (st->objs[i].enable) update_obj(st, &st->objs[i]); } static void draw_objs(struct state *st) { int i; for (i = 0; i < OBJ_MAX; i++) { if (st->objs[i].enable) { int id = st->objs[i].base_pix_id + st->objs[i].add_pix_id; int w = st->objs[i].w; int h = st->objs[i].h; int x = st->objs[i].x - (w / 2); int y = st->objs[i].y - (h / 2); draw_image(st->dpy, st->smallbuf, st->gc, &st->pixs[id], x, y, w, h); } } } /* split pixmap */ static Pixmaps * split_pixmap(Display *dpy, Window win, struct state *st, Pixmaps src, int src_w, int src_h, int div_x, int div_y) { Pixmaps *pixs, *p; GC pix_gc, mask_gc; int w, h; int ix, iy; st->pixs_size = div_x * div_y; pixs = (Pixmaps *)malloc(sizeof(Pixmaps) * st->pixs_size); pix_gc = XCreateGC(dpy, st->image0.pix, 0, 0); mask_gc = XCreateGC(dpy, st->image0.mask, 0, 0); XSetGraphicsExposures(dpy, pix_gc, False); XSetGraphicsExposures(dpy, mask_gc, False); w = src_w / div_x; h = src_h / div_y; p = pixs; for (iy = 0; iy < div_y; iy++) { int y = h * iy; for (ix = 0; ix < div_x; ix++) { int x; x = w * ix; p->pix = XCreatePixmap(dpy, win, w, h, st->xgwa.depth); XCopyArea(dpy, src.pix, p->pix, pix_gc, x, y, w, h, 0, 0); p->mask = XCreatePixmap(dpy, win, w, h, 1); XCopyArea(dpy, src.mask, p->mask, mask_gc, x, y, w, h, 0, 0); p++; } } XFreeGC(dpy, pix_gc); XFreeGC(dpy, mask_gc); return pixs; } /* init images */ static void init_images(Display *dpy, Window win, struct state *st) { Pixmap mask = 0; Pixmap pixmap = image_data_to_pixmap(dpy, win, IMG_DATA, sizeof(IMG_DATA), &st->img_w, &st->img_h, &mask); if (!pixmap) { fprintf(stderr, "%s: Can't load tilemap images\n", progname); exit(1); } st->image0.pix = pixmap; st->image0.mask = mask; /* split pixmap */ st->pixs = split_pixmap(dpy, win, st, st->image0, st->img_w, st->img_h, IMG_W, IMG_H); /* create small amd large screen Pixmaps */ st->smallbuf = XCreatePixmap(dpy, win, OUT_W, OUT_H, st->xgwa.depth); st->largebuf = XCreatePixmap(dpy, win, st->scrw, st->scrh, st->xgwa.depth); XSetForeground(dpy, st->gc, st->bg); XFillRectangle(dpy, st->smallbuf, st->gc, 0, 0, OUT_W, OUT_H); XFillRectangle(dpy, st->largebuf, st->gc, 0, 0, st->scrw, st->scrh); } /* scale Pixmap */ static void scale_pixmap(Display *dpy, Drawable win, Pixmap *src, Pixmap *dst, GC gc, int src_w, int src_h, int dst_w, int dst_h) { float xscale, yscale; int x, y; xscale = (float)src_w / dst_w; yscale = (float)src_h / dst_h; XSetGraphicsExposures(dpy, gc, False); for (x = dst_w - 1; x >= 0; x--) XCopyArea(dpy, *src, *dst, gc, (int)(x * xscale), 0, 1, src_h, x, 0); if (src_h <= dst_h) { for (y = dst_h - 1; y >= 0; y--) XCopyArea(dpy, *dst, *dst, gc, 0, (int)(y * yscale), dst_w, 1, 0, y); } else { for (y = 0; y < dst_h; y++) XCopyArea(dpy, *dst, *dst, gc, 0, (int)(y * yscale), dst_w, 1, 0, y); } } static void draw_tilemap(Display *dpy, GC gc, struct state *st) { int bgw, bgh; int xcnt, ycnt; int n; bgw = BG_CHIP_W * BG_W; bgh = BG_CHIP_H * BG_H; xcnt = (OUT_W / BG_CHIP_W) + 1 + ((OUT_W % BG_CHIP_W == 0) ? 0 : 1); ycnt = (OUT_H / BG_CHIP_H) + 1 + ((OUT_H % BG_CHIP_H == 0) ? 0 : 1); XSetGraphicsExposures(dpy, gc, False); for (n = 0; n < BG_COUNT; n++) { int bx, by, dx, dy, xc, yc; bx = (int)st->bg_x[n]; by = (int)st->bg_y[n]; while (bx < 0) bx += bgw; while (by < 0) by += bgh; bx = bx % bgw; by = by % bgh; dx = bx % BG_CHIP_W; dy = by % BG_CHIP_H; for (yc = 0; yc < ycnt; yc++) { int iy = ((yc * BG_CHIP_H + by) % bgh) / BG_CHIP_H; for (xc = 0; xc < xcnt; xc++) { int ix = ((xc * BG_CHIP_W + bx) % bgw) / BG_CHIP_W; int id = bg_layer[n][(iy % BG_H) * BG_W + (ix % BG_W)]; if (id != 0) { int x, y; x = xc * BG_CHIP_W - dx; y = yc * BG_CHIP_H - dy; draw_image(dpy, st->smallbuf, gc, &st->pixs[id], x, y, BG_CHIP_W, BG_CHIP_H); } } } } XSetClipMask(dpy, gc, None); } static void init_bg_pos(struct state *st) { int i; /* init position */ for (i = 0; i < BG_COUNT; i++) { st->bg_x[i] = 0.0; st->bg_y[i] = 0.0; } /* init spped */ st->bg_dx[BG_COUNT - 1] = (float)st->speed; st->bg_dy[BG_COUNT - 1] = (float)st->speed / 4.0; for (i = BG_COUNT - 2; i >= 0; i--) { st->bg_dx[i] = st->bg_dx[i + 1] / 2.0; st->bg_dy[i] = st->bg_dy[i + 1] / 2.0; } } static void get_target_size(int src_w, int src_h, int scrw, int scrh, int *tgt_w, int *tgt_h) { double xscale, yscale, scale; xscale = (double)scrw / src_w; yscale = (double)scrh / src_h; scale = (xscale <= yscale) ? xscale : yscale; *tgt_w = (int)(src_w * scale); *tgt_h = (int)(src_h * scale); } /* update and draw */ static void update(Display *dpy, Window win, struct state *st) { int src_w = OUT_W; int src_h = OUT_H; int dst_w = st->scrw; int dst_h = st->scrh; int aw, ah; /* update objs */ update_objs(st); XSetGraphicsExposures(dpy, st->gc, False); /* draw tilemap */ draw_tilemap(dpy, st->gc, st); /* draw objs */ draw_objs(st); /* scaling to screen resolution */ get_target_size(src_w, src_h, dst_w, dst_h, &aw, &ah); scale_pixmap(dpy, win, &st->smallbuf, &st->largebuf, st->gc, src_w, src_h, aw, ah); /* draw large Pixmap to backbuffer */ { int x = (dst_w - aw) / 2; int y = (dst_h - ah) / 2; XFillRectangle(dpy, st->backbuf, st->erase_gc, 0, 0, st->scrw, st->scrh); XCopyArea(dpy, st->largebuf, st->backbuf, st->gc, 0, 0, aw, ah, x, y); } /* change bg position */ { int i; for (i = 0; i < BG_COUNT; i++) { st->bg_x[i] += st->bg_dx[i]; st->bg_y[i] += st->bg_dy[i]; } } } /* get parameter */ static void get_parameter(struct state *st) { st->delay = get_integer_resource(st->dpy, "delay", "Integer"); st->speed = get_integer_resource(st->dpy, "speed", "Speed"); st->monochrome = get_boolean_resource(st->dpy, "mono", "Boolean"); /* st->use_dbuf = get_boolean_resource(st->dpy, "doubleBuffer", "Boolean"); */ } static void init_doublebuffer(Display *dpy, Window win, struct state *st) { st->use_dbuf = True; #ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */ st->use_dbuf = False; #endif #ifdef HAVE_DOUBLE_BUFFER_EXTENSION st->dbeclear_p = get_boolean_resource(dpy, "useDBEClear", "Boolean"); #endif st->backbuf = st->ba = st->bb = 0; /* double-buffer to reduce flicker */ #ifdef HAVE_DOUBLE_BUFFER_EXTENSION st->backbuf = st->backb = xdbe_get_backbuffer(dpy, win, XdbeUndefined); #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ if (st->use_dbuf) { /* use double buffer. init */ #ifdef HAVE_DOUBLE_BUFFER_EXTENSION if (st->dbeclear_p) st->backbuf = xdbe_get_backbuffer(dpy, win, XdbeBackground); else st->backbuf = xdbe_get_backbuffer(dpy, win, XdbeUndefined); st->backb = st->backbuf; #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ if (!st->backbuf) { /* create buffer A, B */ st->ba = XCreatePixmap(dpy, win, st->scrw, st->scrh, st->xgwa.depth); st->bb = XCreatePixmap(dpy, win, st->scrw, st->scrh, st->xgwa.depth); st->backbuf = st->ba; } } else { /* not use double buffer */ st->backbuf = win; } /* clear double buffer */ if (st->ba) XFillRectangle(dpy, st->ba, st->erase_gc, 0, 0, st->scrw, st->scrh); if (st->bb) XFillRectangle(dpy, st->bb, st->erase_gc, 0, 0, st->scrw, st->scrh); } static void init_color(Display *dpy, Colormap cmap, struct state *st) { char *cname; cname = get_string_resource(dpy, "textForeground", "Foreground"); if (!cname) cname = strdup("black"); st->fg = get_pixel_resource(dpy, cmap, "foreground", "Foreground"); st->bg = get_pixel_resource(dpy, cmap, "background", "Background"); st->fgcolor.pixel = get_pixel_resource(dpy, cmap, "foreground", "Foreground"); st->bgcolor.pixel = get_pixel_resource(dpy, cmap, "background", "Background"); } /* init graphics context */ static void init_graphics_context(Display *dpy, Window win, struct state *st) { st->gcv.foreground = get_pixel_resource(dpy, st->cmap, "foreground", "Foreground"); st->gc = XCreateGC(dpy, win, 0, &st->gcv); st->gcv.foreground = get_pixel_resource(dpy, st->cmap, "background", "Background"); st->erase_gc = XCreateGC(dpy, win, GCForeground, &st->gcv); } /* init screensaver. Return an object holding your global state. */ static void * hellotilemap_init(Display *dpy, Window win) { struct state *st = (struct state *)calloc(1, sizeof(*st)); st->dpy = dpy; st->win = win; get_parameter(st); /* get window attributes */ XGetWindowAttributes(st->dpy, st->win, &st->xgwa); st->scrw = st->xgwa.width; /* screen width */ st->scrh = st->xgwa.height; /* screen height */ st->cmap = st->xgwa.colormap; /* colormap */ init_color(dpy, st->cmap, st); init_graphics_context(dpy, win, st); init_doublebuffer(dpy, win, st); init_images(dpy, win, st); init_bg_pos(st); init_objs(st); return st; } /* draw single frame. */ static unsigned long hellotilemap_draw(Display *dpy, Window win, void *closure) { struct state *st = (struct state *)closure; /* clear fill back buffer */ #ifdef HAVE_DOUBLE_BUFFER_EXTENSION if (!st->dbeclear_p || !st->backb) XFillRectangle(dpy, st->backbuf, st->erase_gc, 0, 0, st->scrw, st->scrh); #else XFillRectangle(dpy, st->backbuf, st->erase_gc, 0, 0, st->scrw, st->scrh); #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ /* update and draw */ update(dpy, win, st); /* draw double buffer */ #ifdef HAVE_DOUBLE_BUFFER_EXTENSION if (st->backb) { XdbeSwapInfo info[1]; info[0].swap_window = st->win; info[0].swap_action = (st->dbeclear_p ? XdbeBackground : XdbeUndefined); XdbeSwapBuffers(dpy, info, 1); } else #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ if (st->use_dbuf) { XCopyArea(dpy, st->backbuf, st->win, st->gc, 0, 0, st->scrw, st->scrh, 0, 0); st->backbuf = (st->backbuf == st->ba ? st->bb : st->ba); } return st->delay; } /* Called when the window is resized. */ static void hellotilemap_reshape(Display *dpy, Window win, void *closure, unsigned int w, unsigned int h) { struct state *st = (struct state *)closure; XGetWindowAttributes(dpy, win, &st->xgwa); st->scrw = st->xgwa.width; st->scrh = st->xgwa.height; /* recreate large screen Pixmap */ { if (st->largebuf) XFreePixmap(dpy, st->largebuf); st->largebuf = XCreatePixmap(dpy, win, st->scrw, st->scrh, st->xgwa.depth); XSetForeground(dpy, st->gc, st->bg); XFillRectangle(dpy, st->largebuf, st->gc, 0, 0, st->scrw, st->scrh); } if (!st->use_dbuf) { /* #### more complicated if we have a back buffer... */ XClearWindow(dpy, win); } } /* Called when a keyboard or mouse event happens. */ static Bool hellotilemap_event(Display *dpy, Window win, void *closure, XEvent *event) { return False; } /* Free everything you've allocated. */ static void hellotilemap_free(Display *dpy, Window win, void *closure) { struct state *st = (struct state *)closure; XFreeGC(dpy, st->erase_gc); XFreeGC(dpy, st->gc); /* free double buffer */ if (st->ba) XFreePixmap(dpy, st->ba); if (st->bb) XFreePixmap(dpy, st->bb); /* free source image */ if (st->image0.pix) XFreePixmap(dpy, st->image0.pix); if (st->image0.mask) XFreePixmap(dpy, st->image0.mask); /* free small and large buffer */ if (st->smallbuf) XFreePixmap(dpy, st->smallbuf); if (st->largebuf) XFreePixmap(dpy, st->largebuf); /* free split pixmaps */ { int i; for (i = 0; i < st->pixs_size; i++) { if (st->pixs[i].pix) XFreePixmap(dpy, st->pixs[i].pix); if (st->pixs[i].mask) XFreePixmap(dpy, st->pixs[i].mask); } free(st->pixs); } free(st); } /* Default values for the resources you use. */ static const char *hellotilemap_defaults[] = { ".background: black", ".foreground: white", "*delay: 16000", "*speed: 4", "*fpsSolid: true", "*program: xscreensaver-text", "*usePty: False", #ifdef HAVE_DOUBLE_BUFFER_EXTENSION "*useDBE: True", #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ 0}; /* The command-line options you accept. */ static XrmOptionDescRec hellotilemap_options[] = { {"-delay", ".delay", XrmoptionSepArg, 0}, {"-speed", ".speed", XrmoptionSepArg, 0}, {"-program", ".program", XrmoptionSepArg, 0}, {0, 0, 0, 0}}; XSCREENSAVER_MODULE("Hellotilemap", hellotilemap)