/***************************************************************************** CLOCKTAIL: Has a "dragging" clock follow the mouse pointer. Idea nicked from some webpage somewhere. Written by Neil, last update Nov 2003 *****************************************************************************/ #include #include #include #include #define HOURS 12 #define SEC_HAND_POINTS 4 #define MIN_HAND_POINTS 3 #define HOUR_HAND_POINTS 2 #define CENTRE_POINT HOURS #define SEC_HAND_POINT_START (CENTRE_POINT+1) #define MIN_HAND_POINT_START (SEC_HAND_POINT_START+SEC_HAND_POINTS) #define HOUR_HAND_POINT_START (MIN_HAND_POINT_START+MIN_HAND_POINTS) #define POINTS (HOURS+SEC_HAND_POINTS+MIN_HAND_POINTS+HOUR_HAND_POINTS+1) #define CLOCK_RADIUS 100 #define DOT_WIDTH 20 #define HOUR_DOT_WIDTH_MULT 1.3 #define DEGS_PER_RADIAN 57.29578 #define FULL_CIRCLE 23040 /* 360 * 24 */ /* Globals */ Display *display; char *disp; Window rootwin,rw,cw; GC gc; float xpos[POINTS],ypos[POINTS]; float xadd[POINTS],yadd[POINTS]; float clock_radius; float dot_width; float ang; int delay; /* Forward declarations */ void parse_command_line(int argc, char **argv); void setup_X(); void setup_static(); void mainloop(); /*** Start here ***/ main(int argc, char **argv) { parse_command_line(argc,argv); setup_X(); setup_static(); mainloop(); } /*** Parse the command line parameters ***/ void parse_command_line(int argc, char **argv) { int i,o; char *opt[] = { "d","u","cw","dw" }; enum { OPT_DISP, OPT_DELAY, OPT_CW, OPT_DW, OPT_END }; /* Set up some default values */ disp = NULL; delay = 60000; clock_radius = CLOCK_RADIUS; dot_width = DOT_WIDTH; /* Parse command line */ for(i=1;i < argc;++i) { if (argv[i][0] != '-' || i == argc-1) goto USAGE; for(o=0;o != OPT_END;++o) if (!strcasecmp(argv[i]+1,opt[o])) break; if (o == OPT_END) goto USAGE; switch(o) { case OPT_DISP: disp = argv[++i]; break; case OPT_DELAY: delay = atoi(argv[++i]); break; case OPT_CW: clock_radius = atof(argv[++i]) / 2; break; case OPT_DW: dot_width = atof(argv[++i]); break; default: goto USAGE; } } return; USAGE: printf("Usage: %s\n" " [-d ]\n" " [-u ]\n" " [-cw ]\n" " [-dw ]\n",argv[0]); exit(1); } /*** Setup X ***/ void setup_X() { XGCValues gcvals; int screen; /* Set up X */ if (!(display=XOpenDisplay(disp))) { fprintf(stderr,"XOpenDisplay(): Can't connect to: %s\n", XDisplayName(disp)); exit(1); } screen=DefaultScreen(display); rootwin=RootWindow(display,screen); /* Create GC. IncludeInferiors means that when we draw to root window it will draw over the top of any child windows in it. */ gcvals.function = GXinvert; gcvals.subwindow_mode = IncludeInferiors; gc=XCreateGC(display,rootwin,GCFunction | GCSubwindowMode, &gcvals); XSelectInput(display,rootwin,ExposureMask); } /*** Setup the static data ***/ void setup_static() { float px,py; int anginc,i; /* Setup static data */ xpos[0] = 0; ypos[0] = 0; xadd[0] = 0; yadd[0] = 0; px = 0; py = 0; anginc = 360/HOURS; /* Do hour markers */ for(i=1,ang=270+anginc;i < HOURS;++i,ang+=anginc) { xpos[i] = sin(ang / DEGS_PER_RADIAN) * clock_radius + clock_radius; ypos[i] = cos(ang / DEGS_PER_RADIAN) * clock_radius; xadd[i] = xpos[i] - px; yadd[i] = py - ypos[i]; px = xpos[i]; py = ypos[i]; } /* Centre point */ xadd[CENTRE_POINT] = -sin((270-anginc) / DEGS_PER_RADIAN) * clock_radius; yadd[CENTRE_POINT] = cos((270-anginc) / DEGS_PER_RADIAN) * clock_radius; } /*** Do the business ***/ void mainloop() { Window rw,cw; u_int pmask; time_t thetime; struct tm *tms; float xa,ya,dr; int dispcnt; int mx,my,wx,wy,i; /* Mainloop */ for(dispcnt=0;;++dispcnt) { /* Get mouse position */ XQueryPointer(display,rootwin,&rw,&cw,&mx,&my,&wx,&wy,&pmask); /* Set up hands */ time(&thetime); tms = localtime(&thetime); xa = sin(tms->tm_sec*6 / DEGS_PER_RADIAN) * clock_radius / (SEC_HAND_POINTS+1); ya = -cos(tms->tm_sec*6 / DEGS_PER_RADIAN) * clock_radius / (SEC_HAND_POINTS+1); for(i=0;i < SEC_HAND_POINTS;++i) { xadd[SEC_HAND_POINT_START+i] = xa; yadd[SEC_HAND_POINT_START+i] = ya; } xa = sin(tms->tm_min*6 / DEGS_PER_RADIAN) * clock_radius / (MIN_HAND_POINTS+1); ya = -cos(tms->tm_min*6 / DEGS_PER_RADIAN) * clock_radius / (MIN_HAND_POINTS+1); for(i=0;i < MIN_HAND_POINTS;++i) { xadd[MIN_HAND_POINT_START+i] = xa; yadd[MIN_HAND_POINT_START+i] = ya; } xa = sin(tms->tm_hour*30 / DEGS_PER_RADIAN) * clock_radius / (HOUR_HAND_POINTS+1); ya = -cos(tms->tm_hour*30 / DEGS_PER_RADIAN) * clock_radius / (HOUR_HAND_POINTS+1); for(i=0;i < HOUR_HAND_POINTS;++i) { xadd[HOUR_HAND_POINT_START+i] = xa; yadd[HOUR_HAND_POINT_START+i] = ya; } /* Draw stuff , but don't do it until we've updated all positions to their start position or we'll get garbage on the screen */ for(i=POINTS-1;i >= 0;--i) { /* Make the 12,3,6 & 9 hour points slightly larger and the second hand points slighlty smaller */ switch(i) { case 0: case 3: case 6: case 9: dr = dot_width * HOUR_DOT_WIDTH_MULT; break; default: dr = (i >= SEC_HAND_POINT_START && i < MIN_HAND_POINT_START) ? dot_width * 0.6 : dot_width; } /* Undraw old dot/circle. +1 is here so that draw happens first rather than an undraw when first displaying clock */ if (dispcnt > POINTS+1) XFillArc( display,rootwin,gc, (int)xpos[i],(int)ypos[i], (u_int)dr,(u_int)dr,0,FULL_CIRCLE); /* If we're not at the last point move all points relative to the one in front */ if (i) { switch(i) { /* The clock hands all sprout from the centre point , not from each other */ case SEC_HAND_POINT_START: case MIN_HAND_POINT_START: case HOUR_HAND_POINT_START: xpos[i] = xpos[CENTRE_POINT] + xadd[i]; ypos[i] = ypos[CENTRE_POINT] + yadd[i]; break; default: xpos[i] = xpos[i-1] + xadd[i]; ypos[i] = ypos[i-1] + yadd[i]; } } else { /* Move first point with mouse pointer */ xpos[0] = mx + 5; ypos[0] = my - dot_width * HOUR_DOT_WIDTH_MULT / 2; } if (dispcnt > POINTS) XFillArc( display,rootwin,gc, (int)xpos[i],(int)ypos[i], (u_int)dr,(u_int)dr,0,FULL_CIRCLE); } XFlush(display); usleep(delay); } }