Devuan fork of gpsd
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Tachometer.c 18 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. /*
  2. * Tachometer Widget Implementation
  3. *
  4. * Author: Kazuhiko Shutoh, 1989.
  5. * Revised by Shinji Sumimoto, 1989/9 (xtachos)
  6. * Modifications : ilham@mit.edu (July 10 '90)
  7. * Cleaned up and simplified by Eric S. Raymond, December 2004.
  8. *
  9. * This file is Copyright (c) 2004-2018 by the GPSD project
  10. * SPDX-License-Identifier: BSD-2-clause
  11. */
  12. #include "gpsd_config.h" /* must be before all includes */
  13. #include <X11/IntrinsicP.h>
  14. #include <X11/StringDefs.h>
  15. #include <TachometerP.h>
  16. #include <math.h>
  17. #include "gps.h"
  18. #define D2R 0.0174532925199432957692369076848861271 /* radians = pi/180 */
  19. /****************************************************************
  20. *
  21. * Full class record constant
  22. *
  23. ****************************************************************/
  24. typedef struct
  25. {
  26. unsigned char digit[7];
  27. } DigitRec;
  28. typedef struct
  29. {
  30. int nofline;
  31. XPoint point_list[5];
  32. } StringRec;
  33. /* Number character database - like an LED */
  34. static DigitRec num_segment[] = {
  35. {{1, 1, 1, 1, 1, 1, 0}},
  36. {{0, 1, 1, 0, 0, 0, 0}},
  37. {{1, 1, 0, 1, 1, 0, 1}},
  38. {{1, 1, 1, 1, 0, 0, 1}},
  39. {{0, 1, 1, 0, 0, 1, 1}},
  40. {{1, 0, 1, 1, 0, 1, 1}},
  41. {{1, 0, 1, 1, 1, 1, 1}},
  42. {{1, 1, 1, 0, 0, 0, 0}},
  43. {{1, 1, 1, 1, 1, 1, 1}},
  44. {{1, 1, 1, 1, 0, 1, 1}}
  45. };
  46. static XSegment offset[] = {
  47. {-10, -10, 10, -10},
  48. {10, -10, 10, 0},
  49. {10, 0, 10, 10},
  50. {10, 10, -10, 10},
  51. {-10, 10, -10, 0},
  52. {-10, 0, -10, -10},
  53. {-10, 0, 10, 0}
  54. };
  55. /* " X 10 %" character database */
  56. static StringRec char_data[] = {
  57. {2, /* "X" */
  58. {{-17, -5},
  59. {-7, 5}}},
  60. {2,
  61. {{-7, -5},
  62. {-17, 5}}},
  63. {2, /* "1" */
  64. {{-2, -5},
  65. {-2, 5}}},
  66. {5, /* "0" */
  67. {{2, -5},
  68. {12, -5},
  69. {12, 5},
  70. {2, 5},
  71. {2, -5}}}
  72. };
  73. #define offst(field) XtOffset(TachometerWidget, field)
  74. static XtResource resources[] = {
  75. {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
  76. offst(tachometer.scale), XtRString, "XtDefaultForeground"}
  77. ,
  78. {XtNtachometerCircleColor, XtCBorderColor, XtRPixel, sizeof(Pixel),
  79. offst(tachometer.circle), XtRString, "XtDefaultForeground"}
  80. ,
  81. {XtNtachometerNeedleColor, XtCBorderColor, XtRPixel, sizeof(Pixel),
  82. offst(tachometer.needle), XtRString, "XtDefaultForeground"}
  83. ,
  84. {XtNtachometerNeedleSpeed, XtCtachometerNeedleSpeed, XtRInt,
  85. sizeof(int), offst(tachometer.speed), XtRImmediate, (caddr_t) 1},
  86. {XtNvalue, XtCValue, XtRInt, sizeof(int),
  87. offst(tachometer.value), XtRImmediate, (caddr_t) 0},
  88. {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
  89. offst(core.height), XtRImmediate, (caddr_t) 100}
  90. ,
  91. {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
  92. offst(core.width), XtRImmediate, (caddr_t) 100}
  93. ,
  94. {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension),
  95. offst(core.border_width), XtRImmediate, (caddr_t) 0}
  96. ,
  97. {XtNinternalBorderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension),
  98. offst(tachometer.internal_border), XtRImmediate, (caddr_t) 0}
  99. ,
  100. };
  101. static void Initialize(Widget request, Widget new),
  102. Realize(Widget w, Mask * valueMask, XSetWindowAttributes * attributes),
  103. Resize(Widget w), Redisplay(Widget w, XEvent * event, Region region),
  104. Destroy(Widget w);
  105. static Boolean SetValues(Widget current, Widget request UNUSED, Widget new);
  106. TachometerClassRec tachometerClassRec = {
  107. {
  108. /* core_class fields */
  109. #define superclass (&simpleClassRec)
  110. /* superclass */ (WidgetClass) superclass,
  111. /* class_name */ "Tachometer",
  112. /* widget_size */ sizeof(TachometerRec),
  113. /* class_initialize */ NULL,
  114. /* class_part_initialize */ NULL,
  115. /* class_inited */ FALSE,
  116. /* initialize */ (XtInitProc) Initialize,
  117. /* initialize_hook */ NULL,
  118. /* realize */ Realize,
  119. /* actions */ NULL,
  120. /* num_actions */ 0,
  121. /* resources */ resources,
  122. /* num_resources */ XtNumber(resources),
  123. /* xrm_class */ NULLQUARK,
  124. /* compress_motion */ TRUE,
  125. /* compress_exposure */ TRUE,
  126. /* compress_enterleave */ TRUE,
  127. /* visible_interest */ FALSE,
  128. /* destroy */ Destroy,
  129. /* resize */ Resize,
  130. /* expose */ Redisplay,
  131. /* set_values */ (XtSetValuesFunc) SetValues,
  132. /* set_values_hook */ NULL,
  133. /* set_values_almost */ XtInheritSetValuesAlmost,
  134. /* get_values_hook */ NULL,
  135. /* accept_focus */ NULL,
  136. /* version */ XtVersion,
  137. /* callback_private */ NULL,
  138. /* tm_table */ NULL,
  139. /* query_geometry */ NULL,
  140. /* display_accelerator */ XtInheritDisplayAccelerator,
  141. /* extension */ NULL
  142. }
  143. ,
  144. /* Simple class fields initialization */
  145. {
  146. /* change_sensitive */ XtInheritChangeSensitive
  147. }
  148. };
  149. WidgetClass tachometerWidgetClass = (WidgetClass) & tachometerClassRec;
  150. /* Private procedures */
  151. static void FastFillCircle(Display * d, Drawable w, GC gc,
  152. Cardinal center_x, Cardinal center_y,
  153. Cardinal radius_x, Cardinal radius_y)
  154. {
  155. XPoint points[360];
  156. Cardinal angle;
  157. for (angle = 0; angle < 360; angle++) {
  158. points[angle].x = (short)(sin((double)angle * D2R) *
  159. (double)radius_x + (double)center_x);
  160. points[angle].y = (short)(cos((double)angle * D2R) *
  161. (double)radius_y + (double)center_y);
  162. }
  163. (void)XFillPolygon(d, w, gc, points, 360, Complex, CoordModeOrigin);
  164. }
  165. static void DrawSingleNumber(TachometerWidget w, int which, Cardinal x,
  166. Cardinal y)
  167. {
  168. XSegment segments[7];
  169. Cardinal nsegments, width, height, count;
  170. width = (Cardinal) ((w->core.width / 2) - w->tachometer.internal_border);
  171. height =
  172. (Cardinal) ((w->core.height / 2) - w->tachometer.internal_border);
  173. if ((width == 0) || (height == 0))
  174. return;
  175. for (count = 0, nsegments = 0; count < 7; count++)
  176. if (num_segment[which].digit[count] == 1) {
  177. segments[nsegments].x1 = (short)
  178. (x + ((double)offset[count].x1 * ((double)width / 200.0)));
  179. segments[nsegments].y1 = (short)
  180. (y + ((double)offset[count].y1 * ((double)height / 200.0)));
  181. segments[nsegments].x2 = (short)
  182. (x + ((double)offset[count].x2 * ((double)width / 200.0)));
  183. segments[nsegments].y2 = (short)
  184. (y + ((double)offset[count].y2 * ((double)height / 200.0)));
  185. nsegments++;
  186. }
  187. (void)XDrawSegments(XtDisplay(w), XtWindow(w),
  188. w->tachometer.scale_GC, segments, (int)nsegments);
  189. }
  190. static void DrawNumbers(TachometerWidget w, int which, Cardinal x, Cardinal y)
  191. {
  192. if (which == 10) {
  193. DrawSingleNumber(w, 1, (Cardinal) ((double)x * 0.9), y);
  194. DrawSingleNumber(w, 0, x, y);
  195. } else
  196. DrawSingleNumber(w, which, x, y);
  197. }
  198. static void DrawLabelString(TachometerWidget w)
  199. {
  200. XPoint points[5];
  201. int char_count, data_count;
  202. Cardinal ry, center_x, center_y, radius_x, radius_y;
  203. GC gc;
  204. center_x = (Cardinal) (w->core.width / 2);
  205. center_y = (Cardinal) (w->core.height / 2);
  206. radius_x = center_x - w->tachometer.internal_border;
  207. radius_y = center_y - w->tachometer.internal_border;
  208. if (!(center_x != 0 && center_y != 0 && (radius_x > 0) && (radius_y > 0)))
  209. return;
  210. ry = (Cardinal) (radius_y * 0.35 + center_y);
  211. gc = w->tachometer.scale_GC;
  212. for (char_count = 0; char_count < 4; char_count++) {
  213. for (data_count = 0; data_count < char_data[char_count].nofline;
  214. data_count++) {
  215. points[data_count].x = (short)
  216. ((char_data[char_count].point_list[data_count].x) *
  217. (double)radius_x * 0.01 + center_x);
  218. points[data_count].y = (short)
  219. ((char_data[char_count].point_list[data_count].y) *
  220. (double)radius_y * 0.01 + ry);
  221. }
  222. (void)XDrawLines(XtDisplay(w), XtWindow(w), gc, points,
  223. char_data[char_count].nofline, CoordModeOrigin);
  224. }
  225. }
  226. static void DrawGauge(TachometerWidget w)
  227. {
  228. XPoint points[4];
  229. Cardinal in_gauge_x, in_gauge_y, out_gauge_x, out_gauge_y;
  230. Cardinal number_x, number_y, center_x, center_y, radius_x, radius_y;
  231. GC gc;
  232. double step, jump = 1.0;
  233. center_x = w->core.width / 2;
  234. center_y = w->core.height / 2;
  235. radius_x = center_x - w->tachometer.internal_border;
  236. radius_y = center_y - w->tachometer.internal_border;
  237. if ((center_x == 0) || (center_y == 0) || (radius_x <= 0)
  238. || (radius_y <= 0))
  239. return; /* Can't draw anything */
  240. gc = w->tachometer.scale_GC;
  241. for (step = 330.0; step >= 30.0; step -= jump) {
  242. if ((Cardinal) (step) % 30 == 0) {
  243. points[0].x =
  244. sin((step + 1.0) * D2R) * radius_x * 0.75 + center_x;
  245. points[0].y =
  246. cos((step + 1.0) * D2R) * radius_y * 0.75 + center_y;
  247. points[1].x =
  248. sin((step - 1.0) * D2R) * radius_x * 0.75 + center_x;
  249. points[1].y =
  250. cos((step - 1.0) * D2R) * radius_y * 0.75 + center_y;
  251. points[2].x =
  252. sin((step - 1.0) * D2R) * radius_x * 0.85 + center_x;
  253. points[2].y =
  254. cos((step - 1.0) * D2R) * radius_y * 0.85 + center_y;
  255. points[3].x =
  256. sin((step + 1.0) * D2R) * radius_x * 0.85 + center_x;
  257. points[3].y =
  258. cos((step + 1.0) * D2R) * radius_y * 0.85 + center_y;
  259. (void)XFillPolygon(XtDisplay(w), XtWindow(w), gc, points, 4,
  260. Complex, CoordModeOrigin);
  261. number_x = sin((step + 1.0) * D2R) * radius_x * 0.65 + center_x;
  262. number_y = cos((step + 1.0) * D2R) * radius_y * 0.65 + center_y;
  263. if ((int)((330.0 - step) / 30.0) == 1)
  264. jump = 3.0;
  265. DrawNumbers(w, (unsigned char)((330.0 - step) / 30.0),
  266. number_x, number_y);
  267. } else {
  268. in_gauge_x = sin(step * D2R) * radius_x * 0.8 + center_x;
  269. in_gauge_y = cos(step * D2R) * radius_y * 0.8 + center_y;
  270. out_gauge_x = sin(step * D2R) * radius_x * 0.85 + center_x;
  271. out_gauge_y = cos(step * D2R) * radius_y * 0.85 + center_y;
  272. (void)XDrawLine(XtDisplay(w), XtWindow(w), gc, in_gauge_x,
  273. in_gauge_y, out_gauge_x, out_gauge_y);
  274. }
  275. }
  276. DrawLabelString(w);
  277. }
  278. static void DrawNeedle(TachometerWidget w, int load)
  279. {
  280. XPoint points[6];
  281. double cur_theta1, cur_theta2, cur_theta3, cur_theta4, cur_theta5;
  282. Cardinal center_x, center_y, radius_x, radius_y;
  283. center_x = w->core.width / 2;
  284. center_y = w->core.height / 2;
  285. radius_x = center_x - w->tachometer.internal_border;
  286. radius_y = center_y - w->tachometer.internal_border;
  287. if ((center_x == 0) || (center_y == 0) || (radius_x <= 0)
  288. || (radius_y <= 0))
  289. return; /* can't draw anything */
  290. cur_theta1 = (double)(330 - (load * 3)) * D2R;
  291. cur_theta2 = (double)(330 - (load * 3) + 1) * D2R;
  292. cur_theta3 = (double)(330 - (load * 3) - 1) * D2R;
  293. cur_theta4 = (330.0 - ((double)load * 3.0) + 7.0) * D2R;
  294. cur_theta5 = (330.0 - ((double)load * 3.0) - 7.0) * D2R;
  295. points[0].x = (short)(sin(cur_theta1) * radius_x * 0.75 + center_x);
  296. points[0].y = (short)(cos(cur_theta1) * radius_y * 0.75 + center_y);
  297. points[1].x = (short)(sin(cur_theta2) * radius_x * 0.7 + center_x);
  298. points[1].y = (short)(cos(cur_theta2) * radius_y * 0.7 + center_y);
  299. points[2].x = (short)(sin(cur_theta4) * radius_x * 0.1 + center_x);
  300. points[2].y = (short)(cos(cur_theta4) * radius_y * 0.1 + center_y);
  301. points[3].x = (short)(sin(cur_theta5) * radius_x * 0.1 + center_x);
  302. points[3].y = (short)(cos(cur_theta5) * radius_y * 0.1 + center_y);
  303. points[4].x = (short)(sin(cur_theta3) * radius_x * 0.7 + center_x);
  304. points[4].y = (short)(cos(cur_theta3) * radius_y * 0.7 + center_y);
  305. points[5].x = points[0].x;
  306. points[5].y = points[0].y;
  307. (void)XDrawLines(XtDisplay(w), XtWindow(w),
  308. w->tachometer.needle_GC, points, 6, CoordModeOrigin);
  309. }
  310. static void DrawTachometer(TachometerWidget w)
  311. {
  312. Cardinal center_x, center_y, radius_x, radius_y;
  313. center_x = w->core.width / 2;
  314. center_y = w->core.height / 2;
  315. radius_x = center_x - w->tachometer.internal_border;
  316. radius_y = center_y - w->tachometer.internal_border;
  317. if ((center_x == 0) || (center_y == 0) || (radius_x <= 0)
  318. || (radius_y <= 0))
  319. return; /* Can't draw anything -- no room */
  320. /* Big circle */
  321. FastFillCircle(XtDisplay(w), XtWindow(w), w->tachometer.circle_GC,
  322. center_x, center_y, radius_x, radius_y);
  323. /* Inner circle same color as the background */
  324. FastFillCircle(XtDisplay(w), XtWindow(w), w->tachometer.background_GC,
  325. center_x, center_y, (Cardinal) (radius_x * 0.95),
  326. (Cardinal) (radius_y * 0.95));
  327. /* Small circle */
  328. FastFillCircle(XtDisplay(w), XtWindow(w), w->tachometer.circle_GC,
  329. center_x, center_y, (Cardinal) (radius_x * 0.1),
  330. (Cardinal) (radius_y * 0.1));
  331. /* Draw the details */
  332. DrawGauge(w);
  333. DrawNeedle(w, w->tachometer.value);
  334. }
  335. static void MoveNeedle(TachometerWidget w, int new)
  336. {
  337. int step, old, loop;
  338. old = w->tachometer.value;
  339. if (new > 100)
  340. new = 100;
  341. if (old == new)
  342. return;
  343. else if (old < new)
  344. step = (w->tachometer.speed ? w->tachometer.speed : new - old);
  345. else
  346. step = (w->tachometer.speed ? -w->tachometer.speed : new - old);
  347. if (old < new) {
  348. for (loop = old; loop < new; loop += step)
  349. DrawNeedle(w, loop);
  350. for (loop = old + step; loop <= new; loop += step)
  351. DrawNeedle(w, loop);
  352. } else {
  353. for (loop = old; loop > new; loop += step)
  354. DrawNeedle(w, loop);
  355. for (loop = old + step; loop >= new; loop += step)
  356. DrawNeedle(w, loop);
  357. }
  358. if (loop != new + step) /* The final needle wasn't printed */
  359. DrawNeedle(w, new);
  360. w->tachometer.value = new;
  361. }
  362. static void GetneedleGC(TachometerWidget ta)
  363. {
  364. XGCValues values;
  365. values.background = ta->core.background_pixel;
  366. values.foreground = ta->tachometer.needle ^ ta->core.background_pixel;
  367. values.function = GXxor;
  368. ta->tachometer.needle_GC = XtGetGC((Widget) ta,
  369. (unsigned)GCFunction | GCBackground |
  370. GCForeground, &values);
  371. }
  372. static void GetscaleGC(TachometerWidget ta)
  373. {
  374. XGCValues values;
  375. values.foreground = ta->tachometer.scale;
  376. values.background = ta->core.background_pixel;
  377. ta->tachometer.scale_GC = XtGetGC((Widget) ta,
  378. (unsigned)GCForeground | GCBackground,
  379. &values);
  380. }
  381. static void GetcircleGC(TachometerWidget ta)
  382. {
  383. XGCValues values;
  384. values.foreground = ta->tachometer.circle;
  385. values.background = ta->core.background_pixel;
  386. ta->tachometer.circle_GC = XtGetGC((Widget) ta,
  387. (unsigned)GCForeground | GCBackground,
  388. &values);
  389. }
  390. static void GetbackgroundGC(TachometerWidget ta)
  391. {
  392. XGCValues values;
  393. values.foreground = ta->core.background_pixel;
  394. values.background = ta->core.background_pixel;
  395. ta->tachometer.background_GC = XtGetGC((Widget) ta,
  396. (unsigned)GCForeground |
  397. GCBackground, &values);
  398. }
  399. static void Initialize(Widget request UNUSED, Widget new)
  400. {
  401. TachometerWidget ta = (TachometerWidget) new;
  402. GetneedleGC(ta);
  403. GetcircleGC(ta);
  404. GetscaleGC(ta);
  405. GetbackgroundGC(ta);
  406. ta->tachometer.width = ta->tachometer.height = 0;
  407. } /* Initialize */
  408. static void Realize(Widget w, Mask * valueMask,
  409. XSetWindowAttributes * attributes)
  410. {
  411. *valueMask |= CWBitGravity;
  412. attributes->bit_gravity = NorthWestGravity;
  413. (*superclass->core_class.realize) (w, valueMask, attributes);
  414. } /* Realize */
  415. static void Redisplay(Widget w, XEvent * event, Region region UNUSED)
  416. {
  417. if (event->xexpose.count == 0)
  418. DrawTachometer((TachometerWidget) w);
  419. } /* Redisplay */
  420. static void Resize(Widget w)
  421. {
  422. TachometerWidget ta = (TachometerWidget) w;
  423. if ((ta->core.width == ta->tachometer.width) &&
  424. (ta->core.height == ta->tachometer.height))
  425. /* What resize? We don't see a resize! */
  426. return;
  427. (void)XClearWindow(XtDisplay(w), XtWindow(w));
  428. if ((ta->core.width <= ta->tachometer.width) &&
  429. (ta->core.height <= ta->tachometer.height))
  430. /* Only redraw here if no expose events are going to be */
  431. /* generated, i.e. if the window has not grown horizontally */
  432. /* or vertically. */
  433. DrawTachometer(ta);
  434. ta->tachometer.width = ta->core.width;
  435. ta->tachometer.height = ta->core.height;
  436. } /* Resize */
  437. static Boolean SetValues(Widget current, Widget request UNUSED, Widget new)
  438. /* Set specified arguments into widget */
  439. {
  440. Boolean back, changed = (Boolean) False;
  441. TachometerWidget curta = (TachometerWidget) current;
  442. TachometerWidget newta = (TachometerWidget) new;
  443. back = (curta->core.background_pixel != newta->core.background_pixel);
  444. if (back || (curta->tachometer.needle != newta->tachometer.needle)) {
  445. (void)XtReleaseGC(new, newta->tachometer.needle_GC);
  446. GetneedleGC(newta);
  447. changed = True;
  448. }
  449. if (back || (curta->tachometer.scale != newta->tachometer.scale)) {
  450. (void)XtReleaseGC(new, newta->tachometer.scale_GC);
  451. GetscaleGC(newta);
  452. changed = True;
  453. }
  454. if (back || (curta->tachometer.circle != newta->tachometer.circle)) {
  455. (void)XtReleaseGC(new, newta->tachometer.circle_GC);
  456. GetcircleGC(newta);
  457. changed = True;
  458. }
  459. if (back) {
  460. (void)XtReleaseGC(new, newta->tachometer.background_GC);
  461. GetbackgroundGC(newta);
  462. changed = True;
  463. }
  464. if (curta->tachometer.value != newta->tachometer.value) {
  465. MoveNeedle(newta, newta->tachometer.value);
  466. changed = True;
  467. }
  468. return (changed);
  469. }
  470. static void Destroy(Widget w)
  471. {
  472. TachometerWidget ta = (TachometerWidget) w;
  473. (void)XtReleaseGC(w, ta->tachometer.needle_GC);
  474. (void)XtReleaseGC(w, ta->tachometer.circle_GC);
  475. (void)XtReleaseGC(w, ta->tachometer.scale_GC);
  476. (void)XtReleaseGC(w, ta->tachometer.background_GC);
  477. }
  478. /* Exported Procedures */
  479. // cppcheck-suppress unusedFunction
  480. int TachometerGetValue(Widget w)
  481. {
  482. return (((TachometerWidget) w)->tachometer.value);
  483. }
  484. // cppcheck-suppress unusedFunction
  485. int TachometerSetValue(Widget w, int i)
  486. {
  487. int old;
  488. TachometerWidget ta = (TachometerWidget) w;
  489. old = ta->tachometer.value;
  490. MoveNeedle(ta, i);
  491. return (old);
  492. }