/* written by xcomposite (c) 2025-2026 * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* gcc -o glxclock glxclock.c -lX11 -lGL -lm -O3 -ansi */ #define _DEFAULT_SOURCE #define _BSD_SOURCE #include #include #include #include #include #include #include #include #include #include #ifdef M_PI #undef M_PI #endif #define M_PI 3.1415927f Display *dpy; Window win; GLXContext glc; int running = 1; GLuint ticklist; GLuint handlist; GLuint shandlist; #define DRAW_FILLEDHAND() \ glBegin(GL_TRIANGLES); \ glVertex2f(1.0f, 0.0f); \ glVertex2f(0.0f, -1.0f); \ glVertex2f(-0.1f, 0.0f); \ glVertex2f(1.0f, 0.0f); \ glVertex2f(-0.1f, 0.0f); \ glVertex2f(0.0f, 1.0f); \ glEnd(); void buildHandList(int shadow) { if (shadow) { shandlist = glGenLists(1); glNewList(shandlist, GL_COMPILE); glColor3f(0.45,0.45,0.45); DRAW_FILLEDHAND(); glEndList(); return; } handlist = glGenLists(1); glNewList(handlist, GL_COMPILE); glColor3f(0.65,0.65,0.65); DRAW_FILLEDHAND(); glColor3f(0.2,0.2,0.2); glBegin(GL_LINE_LOOP); glVertex2f(1.0, 0.0); glVertex2f(0.0, -1.0); glVertex2f(-.1, 0.0); glVertex2f(0.0, 1.0); glEnd(); glEndList(); } void drawHand(float rad, float wid, float leng, int shadow) { glPushMatrix(); if (shadow) glTranslatef(wid, -wid, 0); glRotatef(rad * 180.0 / M_PI, 0.0f, 0.0f, 1.0f); glScalef(leng, wid, 1.0f); if (shadow) { glCallList(shandlist); glPopMatrix(); return; } glCallList(handlist); glPopMatrix(); } void buildTickList() { float rad; float len; int i; ticklist = glGenLists(1); glNewList(ticklist, GL_COMPILE); /* glColor3f(0, 0, 0); */ glLineWidth(1); glBegin(GL_LINES); for (i = 0; i < 360; i += 6) { if (!(i % 30)) continue; rad = i / 180.0 * M_PI; len = 0.85; glVertex2f(cos(rad)*len, sin(rad)*len); glVertex2f(cos(rad)*0.9, sin(rad)*0.9); } glEnd(); len = 0.8; glLineWidth(3); glBegin(GL_LINES); for (i = 0; i < 360; i += 30) { rad = i / 180.0 * M_PI; glVertex2f(cos(rad)*len, sin(rad)*len); glVertex2f(cos(rad)*0.9, sin(rad)*0.9); } glEnd(); glLineWidth(1); glEndList(); } int obscured = 0; void draw() { time_t t; struct tm *tminfo; float srad; float mrad; float hrad; if (obscured) return; glClear(GL_COLOR_BUFFER_BIT); t = time(NULL); tminfo = localtime(&t); srad = M_PI/2 - (tminfo->tm_sec / 60.0) * 2 * M_PI; mrad = M_PI/2 - ((tminfo->tm_min + tminfo->tm_sec / 60.0) / 60.0) * 2 * M_PI; hrad = M_PI/2 - (((tminfo->tm_hour % 12) + tminfo->tm_min / 60.0 + tminfo->tm_sec / 3600.0) / 12.0) * 2 * M_PI; drawHand(srad, 0.01, 0.98, 1); drawHand(mrad, 0.05, 0.98, 1); drawHand(hrad, 0.05, 0.7, 1); glColor3f(0, 0, 0); glCallList(ticklist); drawHand(hrad, 0.05, 0.7, 0); drawHand(mrad, 0.05, 0.98, 0); drawHand(srad, 0.02, 0.98, 0); glFlush(); } void exitprog() { running = 0; } int main(int argc, char **argv) { int screen; struct timeval tv; struct timeval now; fd_set fds; unsigned int ww = 150, wh = 150; int wx = 0, wy = 0; XEvent xev; Atom wmDeleteMessage; XSetWindowAttributes swa; XVisualInfo *vi; Colormap cmap; int r; XClassHint* classhint; XSizeHints sizehint = {0}; static int vattribs[] = { GLX_RGBA, GLX_ALPHA_SIZE, 0, None }; char c; int flags = 0; char* cval = NULL; while ((c = getopt (argc, argv, "g:u?")) != -1) { switch(c) { case 'g': { cval = optarg; if (!cval) { fputs("Option -g requires geometry value!!!\n", stderr); exit(1); } flags = XParseGeometry(optarg, &wx, &wy, &ww, &wh); break; } help: case 'h': case 'u': case '?': printf("usage: %s [-g ]\n", argv[0]); exit(1); break; default: goto help; exit(1); break; } } dpy = XOpenDisplay(NULL); if(!dpy) { fputs("Cannot open display!!!\n", stderr); exit(1); } screen = DefaultScreen(dpy); vi = glXChooseVisual(dpy, screen, vattribs); if(!vi) { fputs("No appropriate visual found!!\n", stderr); exit(1); } cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone); swa.colormap = cmap; swa.event_mask = ExposureMask | StructureNotifyMask | VisibilityChangeMask; if (wx < 0) wx = DisplayWidth(dpy, screen) - ww + wx; if (wy < 0) wy = DisplayHeight(dpy, screen) - wh + wy; win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), wx, wy, ww, wh, 1, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa); if (flags & XValue) { sizehint.flags = PPosition; sizehint.x = wx; sizehint.y = wy; XSetNormalHints(dpy, win, &sizehint); } XMapWindow(dpy, win); XStoreName(dpy, win, "glxclock"); classhint = XAllocClassHint(); if (classhint) { classhint->res_name = "glxclock"; classhint->res_class = "GLXClock"; XSetClassHint(dpy, win, classhint); XFree(classhint); } wmDeleteMessage = XInternAtom(dpy, "WM_DELETE_WINDOW", False); XSetWMProtocols(dpy, win, &wmDeleteMessage, 1); glc = glXCreateContext(dpy, vi, NULL, GL_TRUE); glXMakeCurrent(dpy, win, glc); glClearColor(1,1,1,1); glDisable(GL_DITHER); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glDisable(GL_LINE_SMOOTH); glDisable(GL_POLYGON_SMOOTH); glDisable(GL_CULL_FACE); glDisable(GL_STENCIL_TEST); glDisable(GL_TEXTURE_3D); glDisable(GL_FOG); glDisable(GL_SCISSOR_TEST); glDisable(GL_MULTISAMPLE); glDisable(GL_COLOR_MATERIAL); glDisable(GL_NORMALIZE); glOrtho(-1, 1, -1, 1, -1, 1); buildTickList(); buildHandList(0); buildHandList(1); glViewport(0, 0, ww, wh); signal(SIGINT, exitprog); signal(SIGHUP, exitprog); signal(SIGTERM, exitprog); while(running) { FD_ZERO(&fds); FD_SET(ConnectionNumber(dpy), &fds); gettimeofday(&now, NULL); tv.tv_sec = 0; tv.tv_usec = 1020000 - now.tv_usec; while(XPending(dpy)) { XNextEvent(dpy, &xev); switch(xev.type){ case ConfigureNotify: glViewport(0, 0, xev.xconfigure.width, xev.xconfigure.height); draw(); break; case Expose: draw(); break; case MapNotify: obscured = 0; break; case ClientMessage: if ((Atom)xev.xclient.data.l[0] == wmDeleteMessage) { running = 0; goto exit; } break; case VisibilityNotify: if (((XVisibilityEvent*)&xev)->state == VisibilityFullyObscured) obscured = 1; else obscured = 0; break; case UnmapNotify: obscured = 1; break; } } r = select(ConnectionNumber(dpy)+1, &fds, NULL, NULL, &tv); if (r == 0) draw(); } exit: glDeleteLists(ticklist, 1); glDeleteLists(handlist, 1); glDeleteLists(shandlist, 1); glXMakeCurrent(dpy, None, NULL); glXDestroyContext(dpy, glc); XDestroyWindow(dpy, win); XCloseDisplay(dpy); return 0; }