Last active
January 21, 2025 18:32
-
-
Save shmup/19edfe68fa11d2d474b7c600df4f65cf to your computer and use it in GitHub Desktop.
gcc -o vpets vpets.c -lX11 -lXext
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <X11/Xatom.h> | |
#include <X11/Xlib.h> | |
#include <X11/Xutil.h> | |
#include <X11/extensions/shape.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
int main() { | |
Display *display; | |
Window window; | |
XVisualInfo vinfo; | |
XSetWindowAttributes attr; | |
GC gc; | |
XColor color; | |
Colormap colormap; | |
int screen; | |
// circle parameters | |
int circle_x = 0; | |
int circle_y = 0; | |
int circle_dx = 2; | |
int circle_dy = 2; | |
int circle_radius = 40; | |
int circle_diameter = circle_radius * 2; | |
int window_size = circle_diameter + 20; // the circle's bounding box is offset | |
// color parameters | |
unsigned long red = 65535; | |
unsigned long green = 0; | |
unsigned long blue = 0; | |
unsigned long alpha = 32767; // 50% opacity | |
// animation parameters | |
int fps = 120; | |
int sleep_time = 1000000 / fps; | |
// open a connection to the x server | |
display = XOpenDisplay(NULL); | |
if (!display) { | |
fprintf(stderr, "Cannot open display\n"); | |
return 1; | |
} | |
screen = DefaultScreen(display); | |
// get argb visual | |
if (!XMatchVisualInfo(display, screen, 32, TrueColor, &vinfo)) { | |
fprintf(stderr, "No matching visual found\n"); | |
return 1; | |
} | |
// create a colormap | |
colormap = XCreateColormap(display, RootWindow(display, screen), vinfo.visual, | |
AllocNone); | |
// set window attributes | |
attr.colormap = colormap; | |
attr.background_pixel = 0; // transparent background | |
attr.border_pixel = 0; | |
attr.event_mask = StructureNotifyMask; | |
// create a window | |
window = XCreateWindow( | |
display, RootWindow(display, screen), 0, 0, window_size, window_size, 0, | |
vinfo.depth, InputOutput, vinfo.visual, | |
CWColormap | CWBackPixel | CWBorderPixel | CWEventMask, &attr); | |
// set window properties to keep it on top and ignore window manager | |
Atom window_type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); | |
Atom window_type_dock = | |
XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", False); | |
XChangeProperty(display, window, window_type, XA_ATOM, 32, PropModeReplace, | |
(unsigned char *)&window_type_dock, 1); | |
XSetWindowAttributes swa; | |
swa.override_redirect = True; | |
XChangeWindowAttributes(display, window, CWOverrideRedirect, &swa); | |
// create a gc | |
gc = XCreateGC(display, window, 0, NULL); | |
// set a color with transparency (red, 50% opacity) in the pixel value | |
// directly | |
color.pixel = (alpha << 16) | (red << 8) | green; | |
color.red = red; | |
color.green = green; | |
color.blue = blue; | |
color.flags = DoRed | DoGreen | DoBlue; | |
XAllocColor(display, colormap, &color); | |
// create a non-rectangular shape for the window | |
Pixmap mask = XCreatePixmap(display, window, window_size, window_size, 1); | |
GC shape_gc = XCreateGC(display, mask, 0, 0); | |
XSetForeground(display, shape_gc, 0); | |
XFillRectangle(display, mask, shape_gc, 0, 0, window_size, window_size); | |
XSetForeground(display, shape_gc, 1); | |
XFillArc(display, mask, shape_gc, window_size - circle_diameter, | |
window_size - circle_diameter, circle_diameter, circle_diameter, 0, | |
360 * 64); | |
XShapeCombineMask(display, window, ShapeBounding, 0, 0, mask, ShapeSet); | |
XFreeGC(display, shape_gc); | |
XFreePixmap(display, mask); | |
// map the window | |
XMapWindow(display, window); | |
// animation loop | |
while (1) { | |
// move the circle | |
circle_x += circle_dx; | |
circle_y += circle_dy; | |
// bounce off the edges of the screen | |
if (circle_x + window_size > DisplayWidth(display, screen) || | |
circle_x < 0) { | |
circle_dx = -circle_dx; | |
} | |
if (circle_y + window_size > DisplayHeight(display, screen) || | |
circle_y < 0) { | |
circle_dy = -circle_dy; | |
} | |
// clear the window by making a transparent shape | |
XSetForeground(display, gc, 0); | |
XFillRectangle(display, window, gc, 0, 0, window_size, window_size); | |
// set the color and draw the circle | |
XSetForeground(display, gc, color.pixel); | |
XFillArc(display, window, gc, window_size - circle_diameter, | |
window_size - circle_diameter, circle_diameter, circle_diameter, 0, | |
360 * 64); | |
// move the window | |
XMoveWindow(display, window, circle_x, circle_y); | |
// flush the output buffer | |
XFlush(display); | |
// sleep for a short time | |
usleep(sleep_time); | |
} | |
// clean up | |
XFreeGC(display, gc); | |
XDestroyWindow(display, window); | |
XCloseDisplay(display); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment