322 lines
10 KiB
C
322 lines
10 KiB
C
/* -*- Mode: C; fill-column: 79 -*- *******************************************
|
|
*******************************************************************************
|
|
pclock -- a simple analog clock program for the X Window System
|
|
Copyright (C) 1998 Alexander Kourakos
|
|
Time-stamp: <1998-05-28 21:27:58 awk@oxygene.vnet.net>
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
Foundation; either version 2 of the License, or (at your option) any later
|
|
version.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
Author: Alexander Kourakos
|
|
Email: Alexander@Kourakos.com
|
|
Web: http://www.kourakos.com/~awk/pclock/
|
|
*******************************************************************************
|
|
******************************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <math.h>
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/xpm.h>
|
|
#include <X11/extensions/shape.h>
|
|
|
|
#include "PClock.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* The name of the XPM should be ``clock_background''.
|
|
*/
|
|
|
|
#include "Default.xpm"
|
|
|
|
/*****************************************************************************/
|
|
|
|
static Display *display;
|
|
static Window root, window, icon_window, main_window;
|
|
static GC gc;
|
|
static Atom wm_delete_window;
|
|
static Pixel back_pixel, hand_pixel, second_hand_pixel;
|
|
static Pixmap back_pm, mask_pm, all_pm;
|
|
static int old_hour = 0, old_minute = 0, old_second = 0;
|
|
|
|
/*****************************************************************************/
|
|
|
|
static Pixel GetColor(char *);
|
|
static void Redraw(void);
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
CreateWindow(int ac, char *av[])
|
|
{
|
|
int zero = 0;
|
|
char *app_name = NAME;
|
|
char *app_class = CLASS;
|
|
int screen;
|
|
XSizeHints size_hints;
|
|
XWMHints wm_hints;
|
|
XClassHint class_hints;
|
|
XTextProperty window_name;
|
|
XGCValues gcv;
|
|
unsigned long gcm;
|
|
int result, use_internal_pixmap;
|
|
|
|
size_hints.flags = PMinSize | PMaxSize | PPosition;
|
|
size_hints.min_width = size_hints.max_width =
|
|
size_hints.min_height = size_hints.max_height = SIZE;
|
|
|
|
display = XOpenDisplay(NULL);
|
|
if (display == NULL) {
|
|
fprintf(stderr, "ERR: could not open display ``%s''..Exiting.\n",
|
|
XDisplayName(NULL));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", FALSE);
|
|
screen = DefaultScreen(display);
|
|
root = RootWindow(display, screen);
|
|
|
|
back_pixel = GetColor("black");
|
|
hand_pixel = GetColor(option.hand_color);
|
|
second_hand_pixel = GetColor(option.second_hand_color);
|
|
|
|
use_internal_pixmap = TRUE;
|
|
|
|
if (option.background_pixmap[0] != '\0') {
|
|
XpmAttributes attributes;
|
|
|
|
attributes.valuemask = XpmSize;
|
|
result = XpmReadFileToPixmap(display, root, option.background_pixmap,
|
|
&back_pm, &mask_pm, &attributes);
|
|
|
|
if (result != XpmSuccess)
|
|
fprintf(stderr, "ERR: trouble loading pixmap\n");
|
|
else if (attributes.width != SIZE || attributes.height != SIZE)
|
|
fprintf(stderr, "ERR: pixmap must be %dx%d\n", SIZE, SIZE);
|
|
else
|
|
use_internal_pixmap = FALSE;
|
|
}
|
|
|
|
if (use_internal_pixmap) {
|
|
result = XpmCreatePixmapFromData(display, root, clock_background,
|
|
&back_pm, &mask_pm, NULL);
|
|
if (result != XpmSuccess) {
|
|
if (result == XpmColorFailed)
|
|
fprintf(stderr, "ERR: unable to allocate pixmap colors..exiting\n");
|
|
else
|
|
fprintf(stderr, "ERR: trouble using built-in pixmap..exiting\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
all_pm = XCreatePixmap(display, root, SIZE, SIZE,
|
|
DefaultDepth(display, screen));
|
|
|
|
XWMGeometry(display, screen, "", NULL, 0, &size_hints, &zero, &zero,
|
|
&size_hints.width, &size_hints.height, &zero);
|
|
|
|
window = XCreateSimpleWindow(display, root,
|
|
0, 0, size_hints.width, size_hints.height,
|
|
0, back_pixel, back_pixel);
|
|
|
|
icon_window = XCreateSimpleWindow(display, root,
|
|
0, 0, size_hints.width, size_hints.height,
|
|
0, back_pixel, back_pixel);
|
|
|
|
wm_hints.window_group = window;
|
|
if (option.under_windowmaker) {
|
|
wm_hints.initial_state = WithdrawnState;
|
|
wm_hints.icon_window = icon_window;
|
|
wm_hints.icon_x = wm_hints.icon_y = 0;
|
|
wm_hints.flags =
|
|
StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;
|
|
main_window = icon_window;
|
|
} else {
|
|
wm_hints.initial_state = NormalState;
|
|
wm_hints.flags = StateHint | WindowGroupHint;
|
|
main_window = window;
|
|
}
|
|
|
|
XSetWMNormalHints(display, window, &size_hints);
|
|
class_hints.res_name = app_name;
|
|
class_hints.res_class = app_class;
|
|
XSetClassHint(display, window, &class_hints);
|
|
XSetClassHint(display, icon_window, &class_hints);
|
|
|
|
XSelectInput(display, main_window, ExposureMask | StructureNotifyMask);
|
|
|
|
if (XStringListToTextProperty(&app_name, 1, &window_name) == 0) {
|
|
fprintf(stderr, "ERR: can't allocate window name..exiting\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
XSetWMName(display, window, &window_name);
|
|
|
|
gcm = GCForeground | GCBackground | GCGraphicsExposures;
|
|
gcv.foreground = hand_pixel;
|
|
gcv.background = back_pixel;
|
|
gcv.graphics_exposures = 0;
|
|
gc = XCreateGC(display, root, gcm, &gcv);
|
|
|
|
XSetWMHints(display, window, &wm_hints);
|
|
XStoreName(display, window, app_name);
|
|
XSetIconName(display, window, app_name);
|
|
XSetCommand(display, window, av, ac);
|
|
XSetWMProtocols(display, main_window, &wm_delete_window, 1);
|
|
XSetWindowBackgroundPixmap(display, main_window, back_pm);
|
|
XShapeCombineMask(display, main_window, ShapeBounding, 0, 0,
|
|
mask_pm, ShapeSet);
|
|
XMapWindow(display, window);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
DestroyWindow(void)
|
|
{
|
|
XFreeGC(display, gc);
|
|
XFreePixmap(display, all_pm);
|
|
XFreePixmap(display, back_pm);
|
|
XFreePixmap(display, mask_pm);
|
|
XDestroyWindow(display, window);
|
|
XDestroyWindow(display, icon_window);
|
|
XCloseDisplay(display);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
UpdateClock(void)
|
|
{
|
|
int h = old_hour, m = old_minute, s;
|
|
struct tm *time_struct;
|
|
time_t curtime;
|
|
double angle;
|
|
int hx, hy, mx, my, sx, sy;
|
|
|
|
curtime = time(NULL);
|
|
time_struct = localtime(&curtime);
|
|
|
|
h = time_struct->tm_hour;
|
|
m = time_struct->tm_min;
|
|
s = time_struct->tm_sec;
|
|
|
|
/*
|
|
* Even if we aren't showing the second hand, redraw anyway so a lost
|
|
* X connection can be detected relatively quickly.
|
|
*/
|
|
|
|
if (h == old_hour && m == old_minute && s == old_second)
|
|
return;
|
|
|
|
XCopyArea(display, back_pm, all_pm, gc, 0, 0, SIZE, SIZE, 0, 0);
|
|
|
|
angle = (M_PI / 6.0) * (double) h + (M_PI / 360.0) * (double) m;
|
|
hx = (SIZE / 2) + (int) ((double) option.hour_hand_length * sin(angle));
|
|
hy = (SIZE / 2) - (int) ((double) option.hour_hand_length * cos(angle));
|
|
|
|
angle = (M_PI / 30.0) * (double) m;
|
|
mx = (SIZE / 2) + (int) ((double) option.minute_hand_length * sin(angle));
|
|
my = (SIZE / 2) - (int) ((double) option.minute_hand_length * cos(angle));
|
|
|
|
XSetLineAttributes(display, gc, option.hand_width, LineSolid, CapRound,
|
|
JoinRound);
|
|
XDrawLine(display, all_pm, gc, SIZE / 2, SIZE / 2, hx, hy);
|
|
XDrawLine(display, all_pm, gc, SIZE / 2, SIZE / 2, mx, my);
|
|
|
|
if (option.show_seconds) {
|
|
angle = (M_PI / 30.0) * (double) s;
|
|
sx = (SIZE / 2) + (int) ((double) option.second_hand_length * sin(angle));
|
|
sy = (SIZE / 2) - (int) ((double) option.second_hand_length * cos(angle));
|
|
|
|
XSetForeground(display, gc, second_hand_pixel);
|
|
XSetLineAttributes(display, gc, option.second_hand_width, LineSolid,
|
|
CapRound, JoinRound);
|
|
XDrawLine(display, all_pm, gc, SIZE / 2, SIZE / 2, sx, sy);
|
|
XSetForeground(display, gc, hand_pixel);
|
|
}
|
|
|
|
Redraw();
|
|
|
|
old_hour = h;
|
|
old_minute = m;
|
|
old_second = s;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
HandleEvents(int *should_quit)
|
|
{
|
|
XEvent event;
|
|
|
|
*should_quit = FALSE;
|
|
|
|
while (XPending(display)) {
|
|
XNextEvent(display, &event);
|
|
switch (event.type) {
|
|
case Expose:
|
|
if (event.xexpose.count == 0)
|
|
Redraw();
|
|
break;
|
|
case DestroyNotify:
|
|
*should_quit = TRUE;
|
|
break;
|
|
case ClientMessage:
|
|
if (event.xclient.data.l[0] == wm_delete_window)
|
|
*should_quit = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
Redraw(void)
|
|
{
|
|
XEvent dummy;
|
|
|
|
while (XCheckTypedWindowEvent(display, main_window, Expose, &dummy)) ;
|
|
XCopyArea(display, all_pm, main_window, gc, 0, 0, SIZE, SIZE, 0, 0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static Pixel
|
|
GetColor(char *name)
|
|
{
|
|
XColor color;
|
|
XWindowAttributes attributes;
|
|
|
|
XGetWindowAttributes(display, root, &attributes);
|
|
|
|
color.pixel = 0;
|
|
if (!XParseColor(display, attributes.colormap, name, &color)) {
|
|
fprintf(stderr, "ERR: can't parse color ``%s''.\n", name);
|
|
} else if (!XAllocColor(display, attributes.colormap, &color)) {
|
|
fprintf(stderr, "ERR: can't allocate ``%s''.\n", name);
|
|
}
|
|
return color.pixel;
|
|
}
|
|
|
|
/******************************************************************************
|
|
*******************************************************************************
|
|
END OF FILE
|
|
*******************************************************************************
|
|
******************************************************************************/
|