/*
* Tachometer Widget Implementation
*
* Author: Kazuhiko Shutoh, 1989.
* Revised by Shinji Sumimoto, 1989/9 (xtachos)
* Modifications : ilham@mit.edu (July 10 '90)
* Cleaned up and simplified by Eric S. Raymond, December 2004.
*
* Permission to use, copy, modify and distribute without charge this software,
* documentation, images, etc. is granted, provided that this comment and the
* author's name is retained. The author assumes no responsibility for lost
* sleep as a consequence of use of this software.
*
* Send any comments, bug reports, etc. to shutoh@isl.yamaha.JUNET
*/
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <TachometerP.h>
#include <math.h>
#include "config.h" /* must define UNUSED */
#define D2R 0.0174532925199432957692369076848861271 /* radians = pi/180 */
/****************************************************************
*
* Full class record constant
*
****************************************************************/
typedef struct {
unsigned char digit[7];
} DigitRec;
typedef struct {
int nofline;
XPoint point_list[5];
} StringRec;
/*@ +charint @*/
/* Number character database - like an LED */
static DigitRec num_segment[] = {
{{1,1,1,1,1,1,0}},
{{0,1,1,0,0,0,0}},
{{1,1,0,1,1,0,1}},
{{1,1,1,1,0,0,1}},
{{0,1,1,0,0,1,1}},
{{1,0,1,1,0,1,1}},
{{1,0,1,1,1,1,1}},
{{1,1,1,0,0,0,0}},
{{1,1,1,1,1,1,1}},
{{1,1,1,1,0,1,1}}};
static XSegment offset[] = {
{-10,-10, 10,-10},
{ 10,-10, 10, 0},
{ 10, 0, 10, 10},
{ 10, 10,-10, 10},
{-10, 10,-10, 0},
{-10, 0,-10,-10},
{-10, 0, 10, 0}};
/*@ -initallelements @*/
/* " X 10 %" character database */
static StringRec char_data[] = {
{ 2, /* "X" */
{{-17, -5},
{-7, 5}}},
{ 2,
{{-7, -5},
{-17, 5}}},
{ 2, /* "1" */
{{-2, -5},
{-2, 5}}},
{ 5, /* "0" */
{{2, -5},
{12, -5},
{12, 5},
{2, 5},
{2, -5}}}};
#if 0
{{{{ {2, -5}}},
{ 5, /* "%" */
{{17, -5},
{20, -5},
{20, -2},
{17, -2},
{17, -5}}},
{ 2,
{{27, -5},
{17, 5}}},
{5,
{{24, 2},
{27, 2},
{27, 5},
{24, 5},
{24, 2}}}};
#endif
/*@ -initallelements @*/
/*@ -charint @*/
/*@ -nullderef -immediatetrans -type -nullassign @*/
#define offst(field) XtOffset(TachometerWidget, field)
static XtResource resources[] = {
{XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
offst(tachometer.scale), XtRString, "XtDefaultForeground"},
{XtNtachometerCircleColor, XtCBorderColor, XtRPixel, sizeof(Pixel),
offst(tachometer.circle), XtRString, "XtDefaultForeground"},
{XtNtachometerNeedleColor, XtCBorderColor, XtRPixel, sizeof(Pixel),
offst(tachometer.needle), XtRString, "XtDefaultForeground"},
{XtNtachometerNeedleSpeed, XtCtachometerNeedleSpeed, XtRInt,
sizeof(int), offst(tachometer.speed), XtRImmediate, (caddr_t) 1},
{XtNvalue, XtCValue, XtRInt, sizeof(int),
offst(tachometer.value), XtRImmediate, (caddr_t) 0},
{XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
offst(core.height), XtRImmediate, (caddr_t) 100},
{XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
offst(core.width), XtRImmediate, (caddr_t) 100},
{XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension),
offst(core.border_width), XtRImmediate, (caddr_t) 0},
{XtNinternalBorderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension),
offst(tachometer.internal_border), XtRImmediate, (caddr_t) 0},
};
/*@ -nullderef -immediatetrans +type +nullassign @*/
static void Initialize(Widget request, Widget new),
Realize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes),
Resize(Widget w), Redisplay(Widget w, XEvent *event, Region region),
Destroy(Widget w);
static Boolean SetValues(Widget current, Widget request UNUSED, Widget new);
/*@ -fullinitblock @*/
TachometerClassRec tachometerClassRec = {
{
/* core_class fields */
#define superclass (&simpleClassRec)
/* superclass */ (WidgetClass) superclass,
/* class_name */ "Tachometer",
/* widget_size */ sizeof(TachometerRec),
/* class_initialize */ NULL,
/* class_part_initialize */ NULL,
/* class_inited */ FALSE,
/* initialize */ (XtInitProc) Initialize,
/* initialize_hook */ NULL,
/* realize */ Realize,
/* actions */ NULL,
/* num_actions */ 0,
/* resources */ resources,
/* num_resources */ XtNumber(resources),
/* xrm_class */ NULLQUARK,
/* compress_motion */ TRUE,
/* compress_exposure */ TRUE,
/* compress_enterleave */ TRUE,
/* visible_interest */ FALSE,
/* destroy */ Destroy,
/* resize */ Resize,
/* expose */ Redisplay,
/* set_values */ (XtSetValuesFunc) SetValues,
/* set_values_hook */ NULL,
/* set_values_almost */ XtInheritSetValuesAlmost,
/* get_values_hook */ NULL,
/* accept_focus */ NULL,
/* version */ XtVersion,
/* callback_private */ NULL,
/* tm_table */ NULL,
/* query_geometry */ NULL,
/* display_accelerator */ XtInheritDisplayAccelerator,
/* extension */ NULL
},
/* Simple class fields initialization */
{
/* change_sensitive */ XtInheritChangeSensitive
}
};
/*@ +fullinitblock @*/
WidgetClass tachometerWidgetClass = (WidgetClass)&tachometerClassRec;
/* Private procedures */
static void FastFillCircle(
Display *d, Drawable w, GC gc,
Cardinal center_x, Cardinal center_y, Cardinal radius_x, Cardinal radius_y)
{
/*@ -compdef @*/
XPoint points[360];
Cardinal angle;
for (angle = 0; angle < 360; angle++) {
points[angle].x = (short) (sin((double) angle * D2R) *
(double) radius_x + (double) center_x);
points[angle].y = (short) (cos((double) angle * D2R) *
(double) radius_y + (double) center_y);
}
(void)XFillPolygon(d, w, gc, points, 360, Complex, CoordModeOrigin);
/*@ +compdef @*/
}
static void DrawSingleNumber(TachometerWidget w, int which, Cardinal x, Cardinal y)
{
XSegment segments[7];
Cardinal nsegments, width, height, count;
width = (Cardinal)((w->core.width / 2) - w->tachometer.internal_border);
height = (Cardinal)((w->core.height / 2) - w->tachometer.internal_border);
if ((width == 0) || (height == 0))
return;
/*@ +charint -compdef */
for (count = 0, nsegments = 0; count < 7; count++)
if (num_segment[which].digit[count] == 1) {
segments[nsegments].x1 = (short)
(x + ((double)offset[count].x1 * ((double)width/200.0)));
segments[nsegments].y1 = (short)
(y + ((double)offset[count].y1 * ((double)height/200.0)));
segments[nsegments].x2 = (short)
(x + ((double)offset[count].x2 * ((double)width/200.0)));
segments[nsegments].y2 = (short)
(y + ((double)offset[count].y2 * ((double)height/200.0)));
nsegments++;
}
(void)XDrawSegments(XtDisplay(w), XtWindow(w),
w->tachometer.scale_GC, segments, (int)nsegments);
/*@ -charint +compdef */
}
static void DrawNumbers(TachometerWidget w, int which, Cardinal x, Cardinal y)
{
if (which == 10) {
DrawSingleNumber(w, 1, (Cardinal) ((double) x * 0.9), y);
DrawSingleNumber(w, 0, x, y);
} else
DrawSingleNumber(w, which, x, y);
}
static void DrawLabelString(TachometerWidget w)
{
XPoint points[5];
int char_count, data_count;
Cardinal ry, center_x, center_y, radius_x, radius_y;
GC gc;
center_x = (Cardinal)(w->core.width / 2);
center_y = (Cardinal)(w->core.height / 2);
radius_x = center_x - w->tachometer.internal_border;
radius_y = center_y - w->tachometer.internal_border;
if (!(center_x != 0 && center_y != 0 && (radius_x > 0) && (radius_y > 0)))
return;
ry = (Cardinal)(radius_y * 0.35 + center_y);
gc = w->tachometer.scale_GC;
/*@ -compdef @*/
for (char_count = 0; char_count < 4; char_count++) {
for (data_count = 0; data_count < char_data[char_count].nofline
; data_count++) {
points[data_count].x = (short)
((char_data[char_count].point_list[data_count].x) *
(double) radius_x * 0.01 + center_x);
points[data_count].y = (short)
((char_data[char_count].point_list[data_count].y) *
(double) radius_y * 0.01 + ry);
}
(void)XDrawLines(XtDisplay(w), XtWindow(w), gc, points,
char_data[char_count].nofline, CoordModeOrigin);
}
/*@ +compdef @*/
}
static void DrawGauge(TachometerWidget w)
{
XPoint points[4];
Cardinal in_gauge_x, in_gauge_y, out_gauge_x, out_gauge_y;
Cardinal number_x, number_y, center_x, center_y, radius_x, radius_y;
GC gc;
double step, jump = 1.0;
/*@ -type -unsignedcompare -compdef @*/
center_x = w->core.width / 2; center_y = w->core.height / 2;
radius_x = center_x - w->tachometer.internal_border;
radius_y = center_y - w->tachometer.internal_border;
if ((center_x==0) || (center_y==0) || (radius_x<=0) || (radius_y<=0))
return; /* Can't draw anything */
gc = w->tachometer.scale_GC;
for (step = 330.0; step >= 30.0; step -= jump) {
if ((Cardinal) (step) % 30 == 0) {
points[0].x = sin((step + 1.0)*D2R) * radius_x*0.75 + center_x;
points[0].y = cos((step + 1.0)*D2R) * radius_y*0.75 + center_y;
points[1].x = sin((step - 1.0)*D2R) * radius_x*0.75 + center_x;
points[1].y = cos((step - 1.0)*D2R) * radius_y*0.75 + center_y;
points[2].x = sin((step - 1.0)*D2R) * radius_x*0.85 + center_x;
points[2].y = cos((step - 1.0)*D2R) * radius_y*0.85 + center_y;
points[3].x = sin((step + 1.0)*D2R) * radius_x*0.85 + center_x;
points[3].y = cos((step + 1.0)*D2R) * radius_y*0.85 + center_y;
(void)XFillPolygon(XtDisplay(w), XtWindow(w), gc, points, 4,
Complex, CoordModeOrigin);
number_x = sin((step + 1.0)*D2R) * radius_x*0.65 + center_x;
number_y = cos((step + 1.0)*D2R) * radius_y*0.65 + center_y;
if ((int)((330.0 - step) / 30.0) == 1)
jump = 3.0;
DrawNumbers(w, (unsigned char) ((330.0 - step) / 30.0),
number_x, number_y);
} else {
in_gauge_x = sin(step * D2R) * radius_x * 0.8 + center_x;
in_gauge_y = cos(step * D2R) * radius_y * 0.8 + center_y;
out_gauge_x = sin(step * D2R) * radius_x * 0.85 + center_x;
out_gauge_y = cos(step * D2R) * radius_y * 0.85 + center_y;
(void)XDrawLine(XtDisplay(w), XtWindow(w), gc, in_gauge_x,
in_gauge_y, out_gauge_x, out_gauge_y);
}
}
/*@ +type +unsignedcompare +compdef @*/
DrawLabelString(w);
}
static void DrawNeedle(TachometerWidget w, int load)
{
XPoint points[6];
double cur_theta1, cur_theta2, cur_theta3, cur_theta4, cur_theta5;
Cardinal center_x, center_y, radius_x, radius_y;
/*@ -type -unsignedcompare -compdef @*/
center_x = w->core.width / 2; center_y = w->core.height / 2;
radius_x = center_x - w->tachometer.internal_border;
radius_y = center_y - w->tachometer.internal_border;
if ((center_x==0) || (center_y==0) || (radius_x<=0) || (radius_y<=0))
return; /* can't draw anything */
cur_theta1 = (double) (330 - (load * 3)) * D2R;
cur_theta2 = (double) (330 - (load * 3) + 1) * D2R;
cur_theta3 = (double) (330 - (load * 3) - 1) * D2R;
cur_theta4 = (330.0 - ((double) load * 3.0) + 7.0) * D2R;
cur_theta5 = (330.0 - ((double) load * 3.0) - 7.0) * D2R;
points[0].x = (short)(sin(cur_theta1) * radius_x * 0.75 + center_x);
points[0].y = (short)(cos(cur_theta1) * radius_y * 0.75 + center_y);
points[1].x = (short)(sin(cur_theta2) * radius_x * 0.7 + center_x);
points[1].y = (short)(cos(cur_theta2) * radius_y * 0.7 + center_y);
points[2].x = (short)(sin(cur_theta4) * radius_x * 0.1 + center_x);
points[2].y = (short)(cos(cur_theta4) * radius_y * 0.1 + center_y);
points[3].x = (short)(sin(cur_theta5) * radius_x * 0.1 + center_x);
points[3].y = (short)(cos(cur_theta5) * radius_y * 0.1 + center_y);
points[4].x = (short)(sin(cur_theta3) * radius_x * 0.7 + center_x);
points[4].y = (short)(cos(cur_theta3) * radius_y * 0.7 + center_y);
/*@ -usedef @*/
points[5].x = points[0].x;
points[5].y = points[0].y;
/*@ +usedef @*/
(void)XDrawLines(XtDisplay(w), XtWindow(w),
w->tachometer.needle_GC, points, 6, CoordModeOrigin);
/*@ +type +unsignedcompare +compdef @*/
}
static void DrawTachometer(TachometerWidget w)
{
Cardinal center_x, center_y, radius_x, radius_y;
/*@ -type -unsignedcompare -compdef @*/
center_x = w->core.width / 2; center_y = w->core.height / 2;
radius_x = center_x - w->tachometer.internal_border;
radius_y = center_y - w->tachometer.internal_border;
if ((center_x==0) || (center_y==0) || (radius_x<=0) || (radius_y<=0))
return; /* Can't draw anything -- no room */
/* Big circle */
FastFillCircle(XtDisplay(w), XtWindow(w), w->tachometer.circle_GC,
center_x, center_y, radius_x, radius_y);
/* Inner circle same color as the background */
FastFillCircle(XtDisplay(w), XtWindow(w), w->tachometer.background_GC,
center_x, center_y, (Cardinal) (radius_x * 0.95),
(Cardinal) (radius_y * 0.95));
/* Small circle */
FastFillCircle(XtDisplay(w), XtWindow(w), w->tachometer.circle_GC,
center_x, center_y, (Cardinal) (radius_x * 0.1),
(Cardinal) (radius_y * 0.1));
/* Draw the details */
DrawGauge(w);
DrawNeedle(w, w->tachometer.value);
/*@ +type +unsignedcompare +compdef @*/
}
static void MoveNeedle(TachometerWidget w, int new)
{
int step, old, loop;
old = w->tachometer.value;
if (new > 100)
new = 100;
if (old == new)
return;
else if (old < new)
step = (w->tachometer.speed ? w->tachometer.speed : new - old);
else
step = (w->tachometer.speed ? -w->tachometer.speed : new - old);
/*@ -usedef @*/
if (old < new) {
for (loop = old; loop < new; loop += step)
DrawNeedle(w, loop);
for (loop = old + step; loop <= new; loop += step)
DrawNeedle(w, loop);
} else {
for (loop = old; loop > new; loop += step)
DrawNeedle(w, loop);
for (loop = old + step; loop >= new; loop += step)
DrawNeedle(w, loop);
}
if (loop != new + step) /* The final needle wasn't printed */
DrawNeedle(w, new);
/*@ +usedef @*/
w->tachometer.value = new;
}
static void GetneedleGC(TachometerWidget ta)
{
XGCValues values;
values.background = ta->core.background_pixel;
values.foreground = ta->tachometer.needle ^ ta->core.background_pixel;
values.function = GXxor;
/*@ -type -compdef -mustfreeonly @*/
ta->tachometer.needle_GC = XtGetGC(
(Widget)ta,
(unsigned) GCFunction | GCBackground | GCForeground,
&values);
/*@ +type +compdef +mustfreeonly @*/
}
static void GetscaleGC(TachometerWidget ta)
{
XGCValues values;
values.foreground = ta->tachometer.scale;
values.background = ta->core.background_pixel;
/*@ -type -compdef -mustfreeonly @*/
ta->tachometer.scale_GC = XtGetGC(
(Widget)ta,
(unsigned) GCForeground | GCBackground,
&values);
/*@ +type +compdef +mustfreeonly @*/
}
static void GetcircleGC(TachometerWidget ta)
{
XGCValues values;
values.foreground = ta->tachometer.circle;
values.background = ta->core.background_pixel;
/*@ -type -compdef -mustfreeonly @*/
ta->tachometer.circle_GC = XtGetGC(
(Widget)ta,
(unsigned) GCForeground | GCBackground,
&values);
/*@ +type +compdef +mustfreeonly @*/
}
static void GetbackgroundGC(TachometerWidget ta)
{
XGCValues values;
values.foreground = ta->core.background_pixel;
values.background = ta->core.background_pixel;
/*@ -type -compdef -mustfreeonly @*/
ta->tachometer.background_GC = XtGetGC(
(Widget)ta,
(unsigned) GCForeground | GCBackground,
&values);
/*@ +type +compdef +mustfreeonly @*/
}
static void Initialize(Widget request UNUSED, Widget new)
{
TachometerWidget ta = (TachometerWidget) new;
GetneedleGC(ta);
GetcircleGC(ta);
GetscaleGC(ta);
GetbackgroundGC(ta);
ta->tachometer.width = ta->tachometer.height = 0;
} /* Initialize */
static void Realize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
{
*valueMask |= CWBitGravity;
attributes->bit_gravity = NorthWestGravity;
(*superclass->core_class.realize) (w, valueMask, attributes);
} /* Realize */
static void Redisplay(Widget w, XEvent *event, Region region UNUSED)
{
if (event->xexpose.count == 0)
DrawTachometer((TachometerWidget) w);
} /* Redisplay */
static void Resize(Widget w)
{
TachometerWidget ta = (TachometerWidget) w;
if ((ta->core.width == ta->tachometer.width) &&
(ta->core.height == ta->tachometer.height))
/* What resize? We don't see a resize! */
return;
(void)XClearWindow(XtDisplay(w), XtWindow(w));
if ((ta->core.width <= ta->tachometer.width) &&
(ta->core.height <= ta->tachometer.height))
/* Only redraw here if no expose events are going to be */
/* generated, i.e. if the window has not grown horizontally */
/* or vertically. */
DrawTachometer(ta);
ta->tachometer.width = ta->core.width;
ta->tachometer.height = ta->core.height;
} /* Resize */
static Boolean SetValues(Widget current, Widget request UNUSED, Widget new)
/* Set specified arguments into widget */
{
/*@ -type -boolops -predboolothers @*/
Boolean back, changed = (Boolean)False;
TachometerWidget curta = (TachometerWidget) current;
TachometerWidget newta = (TachometerWidget) new;
back = (curta->core.background_pixel != newta->core.background_pixel);
if (back || (curta->tachometer.needle != newta->tachometer.needle)) {
(void)XtReleaseGC(new, newta->tachometer.needle_GC);
GetneedleGC(newta);
changed = True;
}
if (back || (curta->tachometer.scale != newta->tachometer.scale)) {
(void)XtReleaseGC(new, newta->tachometer.scale_GC);
GetscaleGC(newta);
changed = True;
}
if (back || (curta->tachometer.circle != newta->tachometer.circle)) {
(void)XtReleaseGC(new, newta->tachometer.circle_GC);
GetcircleGC(newta);
changed = True;
}
if (back) {
(void)XtReleaseGC(new, newta->tachometer.background_GC);
GetbackgroundGC(newta);
changed = True;
}
if (curta->tachometer.value != newta->tachometer.value) {
MoveNeedle(newta, newta->tachometer.value);
changed = True;
}
/*@ +type +boolops +predboolothers @*/
return(changed);
}
static void Destroy(Widget w)
{
TachometerWidget ta = (TachometerWidget) w;
(void)XtReleaseGC( w, ta->tachometer.needle_GC );
(void)XtReleaseGC( w, ta->tachometer.circle_GC );
(void)XtReleaseGC( w, ta->tachometer.scale_GC );
(void)XtReleaseGC( w, ta->tachometer.background_GC );
}
/* Exported Procedures */
int TachometerGetValue(Widget w)
{
return(((TachometerWidget) w)->tachometer.value);
}
int TachometerSetValue(Widget w, int i)
{
int old;
TachometerWidget ta = (TachometerWidget) w;
old = ta->tachometer.value;
MoveNeedle(ta, i);
return(old);
}
syntax highlighted by Code2HTML, v. 0.9.1