/*

reinhard@finalmedia.de
Sa 15. Aug 20:36:17 CEST 2026

PUBLIC DOMAIN

simple variant of xeyes. just one eye.
spawn it multiple times, if you need more than an eye.
use scrollwheel to resize.
just use double click or resize below 24 pixels to close.

compile with:

	gcc -O3 xeye.c -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -o xeye -lX11 -lXext -lm

*/
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/shape.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <sys/time.h>
#include <poll.h>

#define INITIAL_WIDTH 64
#define INITIAL_HEIGHT 64
#define DOUBLE_CLICK_TIME 250

#define WHEEL_UP 4
#define WHEEL_DOWN 5
#define TICK_INTERVAL 40

struct MotifWmHints {
    unsigned long flags;
    unsigned long functions;
    unsigned long decorations;
    long          input_mode;
    unsigned long status;
};
#define MWM_HINTS_DECORATIONS (1L << 1)

long long get_time_ms() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (((long long)tv.tv_sec) * 1000) + (tv.tv_usec / 1000);
}

void draw_circle(Display *dpy, Drawable d, GC gc, int cx, int cy, int radius, unsigned long color) {
    XSetForeground(dpy, gc, color);
    XFillArc(dpy, d, gc, cx - radius, cy - radius, radius * 2, radius * 2, 0, 360 * 64);
}

int main() {
    Display *dpy = XOpenDisplay(NULL);
    if (!dpy) {
        fprintf(stderr, "Fehler: X-Display konnte nicht geöffnet werden.\n");
        return 1;
    }

    int screen = DefaultScreen(dpy);
    int shape_event_base, shape_error_base;
    if (!XShapeQueryExtension(dpy, &shape_event_base, &shape_error_base)) {
        fprintf(stderr, "Fehler: XShape-Extension fehlt.\n");
        XCloseDisplay(dpy);
        return 1;
    }

    XSetWindowAttributes attr;
    attr.background_pixmap = None;
    attr.border_pixel = 0;

    attr.event_mask = ExposureMask | StructureNotifyMask | ButtonPressMask | ButtonReleaseMask;

    int current_width = INITIAL_WIDTH;
    int current_height = INITIAL_HEIGHT;

    Window win = XCreateWindow(
        dpy, DefaultRootWindow(dpy),
        100, 100, current_width, current_height, 0,
        CopyFromParent, InputOutput, CopyFromParent,
        CWBackPixmap | CWBorderPixel | CWEventMask, &attr
    );

    XStoreName(dpy, win, "eye");

    Atom wm_hints_atom = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
    if (wm_hints_atom != None) {
        struct MotifWmHints hints;
        hints.flags = MWM_HINTS_DECORATIONS;
        hints.decorations = 0;
        XChangeProperty(dpy, win, wm_hints_atom, wm_hints_atom, 32,
                        PropModeReplace, (unsigned char *)&hints, 5);
    }

    GC gc = XCreateGC(dpy, win, 0, NULL);
    XGCValues gcv;
    GC mask_gc = XCreateGC(dpy, XCreatePixmap(dpy, win, 1, 1, 1), 0, &gcv);

    XMapWindow(dpy, win);

    unsigned long color_white = WhitePixel(dpy, screen);
    unsigned long color_black = BlackPixel(dpy, screen);

    XColor blue_spec;
    XAllocNamedColor(dpy, DefaultColormap(dpy, screen), "blue", &blue_spec, &blue_spec);
    unsigned long color_blue = blue_spec.pixel;

    int running = 1;
    XEvent ev;

    int dragging = 0;
    int drag_start_x = 0;
    int drag_start_y = 0;
    long long last_click_time = 0;

    int win_global_x = 100, win_global_y = 100;
    int w_val = INITIAL_WIDTH, h_val = INITIAL_HEIGHT;

    int last_root_x = -1, last_root_y = -1;
    int last_win_x = -1, last_win_y = -1;
    int last_w = -1, last_h = -1;
    int force_redraw = 1;

    int x11_fd = ConnectionNumber(dpy);
    struct pollfd pfd;
    pfd.fd = x11_fd;
    pfd.events = POLLIN;

    while (running) {
        poll(&pfd, 1, TICK_INTERVAL);

        while (XPending(dpy)) {
            XNextEvent(dpy, &ev);
            if (ev.type == DestroyNotify) {
                running = 0;
            }
            else if (ev.type == Expose) {
                force_redraw = 1;
            }
            else if (ev.type == ConfigureNotify) {
                win_global_x = ev.xconfigure.x;
                win_global_y = ev.xconfigure.y;
                w_val = ev.xconfigure.width;
                h_val = ev.xconfigure.height;
            }
            else if (ev.type == ButtonPress) {
                if (ev.xbutton.button == Button1) {
                    long long current_time = get_time_ms();
                    if (current_time - last_click_time < DOUBLE_CLICK_TIME) {
                        running = 0;
                    } else {
                        dragging = 1;
                        drag_start_x = ev.xbutton.x;
                        drag_start_y = ev.xbutton.y;
                    }
                    last_click_time = current_time;
                }
                else if (ev.xbutton.button == WHEEL_UP) {
                    current_width += 2;
                    current_height += 2;
                    XResizeWindow(dpy, win, current_width, current_height);
                }
                else if (ev.xbutton.button == WHEEL_DOWN) {
                    current_width -= 2;
                    current_height -= 2;
                    if (current_width < 24 || current_height < 24) {
                        running = 0;
                    } else {
                        XResizeWindow(dpy, win, current_width, current_height);
                    }
                }
            }
            else if (ev.type == ButtonRelease) {
                if (ev.xbutton.button == Button1) {
                    dragging = 0;
                }
            }
        }

        if (!running) break;

        Window root_return, child_return;
        int root_x, root_y, win_x, win_y;
        unsigned int mask_return;
        XQueryPointer(dpy, DefaultRootWindow(dpy), &root_return, &child_return,
                      &root_x, &root_y, &win_x, &win_y, &mask_return);

        if (dragging) {
            XMoveWindow(dpy, win, root_x - drag_start_x, root_y - drag_start_y);
        }

        if (root_x == last_root_x && root_y == last_root_y &&
            win_global_x == last_win_x && win_global_y == last_win_y && !force_redraw) {
            continue;
        }

        int win_cx = w_val / 2;
        int win_cy = h_val / 2;
        int eye_r  = (win_cx < win_cy ? win_cx : win_cy) - 4;
        int pup_r  = eye_r / 2;
        if (pup_r < 2) pup_r = 2;

        int eye_global_cx = win_global_x + win_cx;
        int eye_global_cy = win_global_y + win_cy;

        double dx = root_x - eye_global_cx;
        double dy = root_y - eye_global_cy;
        double distance = sqrt(dx * dx + dy * dy);

        int max_move = eye_r - pup_r - 2;
        if (max_move < 0) max_move = 0;
        int pup_cx = win_cx;
        int pup_cy = win_cy;

        if (distance > 0 && max_move > 0) {
            double factor = (distance > max_move) ? (max_move / distance) : 1.0;
            pup_cx += (int)(dx * factor);
            pup_cy += (int)(dy * factor);
        }

        if (w_val != last_w || h_val != last_h || force_redraw) {
            Pixmap mask_buffer = XCreatePixmap(dpy, win, w_val, h_val, 1);
            XSetForeground(dpy, mask_gc, 0);
            XFillRectangle(dpy, mask_buffer, mask_gc, 0, 0, w_val, h_val);
            draw_circle(dpy, mask_buffer, mask_gc, win_cx, win_cy, eye_r, 1);
            XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, mask_buffer, ShapeSet);
            XFreePixmap(dpy, mask_buffer);

            last_w = w_val;
            last_h = h_val;
        }

        Pixmap draw_buffer = XCreatePixmap(dpy, win, w_val, h_val, DefaultDepth(dpy, screen));
        XSetForeground(dpy, gc, color_black);
        XFillRectangle(dpy, draw_buffer, gc, 0, 0, w_val, h_val);
        draw_circle(dpy, draw_buffer, gc, win_cx, win_cy, eye_r, color_white);
        draw_circle(dpy, draw_buffer, gc, pup_cx, pup_cy, pup_r, color_black);
        draw_circle(dpy, draw_buffer, gc, pup_cx, pup_cy, pup_r / 2, color_black);

        XCopyArea(dpy, draw_buffer, win, gc, 0, 0, w_val, h_val, 0, 0);
        XFreePixmap(dpy, draw_buffer);

        last_root_x = root_x; last_root_y = root_y;
        last_win_x = win_global_x; last_win_y = win_global_y;
        force_redraw = 0;

        XFlush(dpy);
    }

    XFreeGC(dpy, mask_gc);
    XFreeGC(dpy, gc);
    XDestroyWindow(dpy, win);
    XCloseDisplay(dpy);
    return 0;
}
