wmail: no longer install into or uninstall from $HOME.
[dockapps.git] / Temperature.app / Temperature.cc
blobe19492c097282f5791af7a6671940591d8fa3b31
1 //
2 // Temperature.app
3 //
4 // Copyright (c) 2000-2002 Per Liden
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307,
19 // USA.
22 #include <X11/Xlib.h>
23 #include <iostream>
24 #include <fstream>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <math.h>
34 #include <time.h>
35 #include "Xpm.h"
36 #include "Temperature.h"
38 #include "pixmaps/main.xpm"
39 #include "pixmaps/redlight.xpm"
41 volatile static ChildStatus childStatus;
43 static void catchBrokenPipe(int sig)
45 exit(0);
48 static void catchChildExit(int sig)
50 int status;
51 waitpid(-1, &status, 0);
53 if (WIFEXITED(status)) {
54 if (WEXITSTATUS(status)) {
55 childStatus = ChildError;
56 } else {
57 childStatus = ChildDone;
59 } else {
60 childStatus = ChildError;
63 if (childStatus == ChildError) {
64 std::cerr << APPNAME << ": could not fetch temperature (wget failed), try option -V for more information" << std::endl;
68 Temperature::Temperature(int argc, char** argv)
70 XClassHint classHint;
71 XSizeHints sizeHints;
72 XWMHints wmHints;
73 Atom deleteWindow;
74 Xpm* image;
75 char* displayName = NULL;
77 mInstanceName = INSTANCENAME;
78 mStationId = 0;
79 mTemperature[0] = 0;
80 mTime[0] = 0;
81 mTimeDiff = 0.0;
82 mFahrenheit = false;
83 mShowTime = false;
84 mTime12HourFormat = false;
85 mVerbose = false;
87 // Parse command line
88 if (argc>1) {
89 for (int i=1; i<argc; i++) {
90 // Display
91 if (!strcmp(argv[i], "-d")) {
92 checkArgument(argv, argc, i);
93 displayName = argv[i+1];
94 i++;
97 // Station id
98 else if (!strcmp(argv[i], "-s")) {
99 checkArgument(argv, argc, i);
100 mStationId = argv[i+1];
101 i++;
104 // Fahrenheit
105 else if (!strcmp(argv[i], "-f")) {
106 mFahrenheit = true;
109 // Time
110 else if (!strcmp(argv[i], "-t")) {
111 mShowTime = true;
112 checkArgument(argv, argc, i);
113 if (!strcmp(argv[i+1], "12")) {
114 mTime12HourFormat = true;
115 } else if (strcmp(argv[i+1], "24")) {
116 std::cerr << APPNAME << ": unknown time format, use 12 or 24" << std::endl;
117 exit(0);
119 i++;
122 // Verbose
123 else if (!strcmp(argv[i], "-V")) {
124 mVerbose = true;
127 // Instance name
128 else if (!strcmp(argv[i], "-n")) {
129 checkArgument(argv, argc, i);
130 mInstanceName = argv[i+1];
131 i++;
134 // Version
135 else if (!strcmp(argv[i], "-v")) {
136 std::cerr << APPNAME << " version " << VERSION << std::endl;
137 exit(0);
140 // Help
141 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
142 showHelp();
143 exit(0);
146 // Unknown option
147 else {
148 std::cerr << APPNAME << ": invalid option '" << argv[i] << "'" << std::endl;
149 tryHelp(argv[0]);
150 exit(0);
155 if (mStationId == 0) {
156 std::cerr << APPNAME << ": you must supply a station id using -s <id>" << std::endl;
157 tryHelp(argv[0]);
158 exit(0);
161 // Open display
162 if ((mDisplay = XOpenDisplay(displayName)) == NULL) {
163 std::cerr << APPNAME << ": could not open display " << displayName << std::endl;
164 exit(0);
167 // Get root window
168 mRoot = RootWindow(mDisplay, DefaultScreen(mDisplay));
170 // Create windows
171 mAppWin = XCreateSimpleWindow(mDisplay, mRoot, 1, 1, 64, 64, 0, 0, 0);
172 mIconWin = XCreateSimpleWindow(mDisplay, mAppWin, 0, 0, 64, 64, 0, 0, 0);
174 // Set classhint
175 classHint.res_name = mInstanceName;
176 classHint.res_class = CLASSNAME;
177 XSetClassHint(mDisplay, mAppWin, &classHint);
179 // Create delete atom
180 deleteWindow = XInternAtom(mDisplay, "WM_DELETE_WINDOW", False);
181 XSetWMProtocols(mDisplay, mAppWin, &deleteWindow, 1);
182 XSetWMProtocols(mDisplay, mIconWin, &deleteWindow, 1);
184 // Set windowname
185 XStoreName(mDisplay, mAppWin, APPNAME);
186 XSetIconName(mDisplay, mAppWin, APPNAME);
188 // Set sizehints
189 sizeHints.flags= USPosition;
190 sizeHints.x = 0;
191 sizeHints.y = 0;
192 XSetWMNormalHints(mDisplay, mAppWin, &sizeHints);
194 // Set wmhints
195 wmHints.initial_state = WithdrawnState;
196 wmHints.icon_window = mIconWin;
197 wmHints.icon_x = 0;
198 wmHints.icon_y = 0;
199 wmHints.window_group = mAppWin;
200 wmHints.flags = StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;
201 XSetWMHints(mDisplay, mAppWin, &wmHints);
203 // Set command
204 XSetCommand(mDisplay, mAppWin, argv, argc);
206 // Set background image
207 image = new Xpm(mDisplay, mRoot, main_xpm);
208 image->setWindowPixmapShaped(mIconWin);
209 delete image;
211 // Create status led
212 mStatusLed = XCreateSimpleWindow(mDisplay, mIconWin, LED_X, LED_Y, 3, 2, 0, 0, 0);
213 image = new Xpm(mDisplay, mRoot, redlight_xpm);
214 image->setWindowPixmap(mStatusLed);
215 delete image;
217 XMapWindow(mDisplay, mIconWin);
218 XMapWindow(mDisplay, mAppWin);
219 XSync(mDisplay, False);
221 // Catch broker pipe signal
222 signal(SIGPIPE, catchBrokenPipe);
224 // Catch child exit signal
225 signal(SIGCHLD, catchChildExit);
228 void Temperature::tryHelp(char* appname)
230 std::cerr << std::endl << "Try `" << appname << " --help' for more information" << std::endl;
233 void Temperature::showHelp()
235 std::cerr << APPNAME << " Copyright (c) 2000-2002 by Per Liden (per@fukt.bth.se)" << std::endl << std::endl
236 << "options:" << std::endl
237 << " -s <id> set station id (ICAO Location Indicator)" << std::endl
238 << " -t 12|24 display time of temperature observation (12 or 24 hour format)" << std::endl
239 << " -f display degrees in Fahrenheit" << std::endl
240 << " -V display verbose messages from wget" << std::endl
241 << " -n <name> set client instance name" << std::endl
242 << " -d <disp> set display" << std::endl
243 << " -v print version and exit" << std::endl
244 << " -h, --help display this help text and exit" << std::endl
245 << std::endl
246 << "You must supply the ICAO Location Indicator (a 4-character string)" << std::endl
247 << "of a weather station near you. You can search for a station on" << std::endl
248 << "this site: http://www.nws.noaa.gov/oso/siteloc.shtml" << std::endl;
251 void Temperature::checkArgument(char** argv, int argc, int index)
253 if (argc-1 < index+1) {
254 std::cerr << APPNAME << ": option '" << argv[index] << "' requires an argument" << std::endl;
255 tryHelp(argv[0]);
256 exit(0);
260 void Temperature::showLed(bool show)
262 if (show) {
263 XMapWindow(mDisplay, mStatusLed);
264 } else {
265 XUnmapWindow(mDisplay, mStatusLed);
267 XSync(mDisplay, False);
270 void Temperature::calcTimeDiff()
272 struct tm* t;
273 double localTime;
274 double universalTime;
275 time_t currentTime;
277 currentTime = time(0);
278 t = gmtime(&currentTime);
279 universalTime = (double)t->tm_hour + (double)t->tm_min / 60.0 + (double)t->tm_sec / 3600.0;
281 currentTime = time(0);
282 t = localtime(&currentTime);
283 localTime = (double)t->tm_hour + (double)t->tm_min / 60.0 + (double)t->tm_sec / 3600.0;
285 mTimeDiff = universalTime - localTime;
286 if (mTimeDiff > 24.0) {
287 mTimeDiff -= 24.0;
288 } else if (mTimeDiff < 0.0) {
289 mTimeDiff += 24.0;
293 void Temperature::setTime(char* utcTime)
295 char unit[3];
296 int hour = 0;
297 int min = 0;
299 strncpy(unit, &utcTime[0], 2);
300 hour = atoi(unit);
301 strncpy(unit, &utcTime[2], 2);
302 min = atoi(unit);
304 double time = ((double)hour + (double)min / 60.0) - mTimeDiff;
305 if (time < 0.0) {
306 time += 24.0;
307 } else if (time > 24.0) {
308 time -= 24.0;
311 hour = (int)time;
312 min = (int)((time - (double)hour) * 60.0 + 0.5);
313 if (min >= 60){
314 min = 0;
315 if (++hour >= 24) {
316 hour = 0;
320 if (mTime12HourFormat) {
321 if (hour >= 0 && hour <= 11) {
322 mTimeAMPM = "AM";
323 } else {
324 mTimeAMPM = "PM";
327 if (hour == 0) {
328 hour = 12;
329 } else if (hour > 12) {
330 hour -= 12;
333 sprintf(mTime, "%d:%.2d", hour, min);
336 bool Temperature::updateTemperture(ifstream& file)
338 const int MAX_LINE = 1024;
339 char buffer[MAX_LINE];
341 if (mShowTime) {
342 // Find time of observation
343 char* start;
344 char time[5];
345 file.getline(buffer, MAX_LINE - 1);
346 file.getline(buffer, MAX_LINE - 1);
347 if ((start = strstr(buffer, "UTC")) == 0) {
348 return false;
350 strncpy(time, start - 5, 4);
351 setTime(time);
354 // Find temperature
355 while (!file.eof()) {
356 file >> buffer;
357 if (!strcmp(buffer, "Temperature:")) {
358 file >> buffer;
359 if (buffer && strlen(buffer) < 5) {
360 char* unit;
361 if (mFahrenheit) {
362 strcpy(mTemperature, buffer);
363 unit = " °F";
364 } else {
365 sprintf(mTemperature, "%d", (int)rint((atoi(buffer) - 32) / 1.8));
366 unit = " °C";
369 Xpm* image = new Xpm(mDisplay, mRoot, main_xpm);
370 if (mShowTime) {
371 if (mTime12HourFormat) {
372 image->drawComposedString(TIME_POS, TIME_FONT, mTime, AMPM_FONT, mTimeAMPM);
373 } else {
374 image->drawString(TIME_POS, TIME_FONT, mTime);
376 image->drawComposedString(TEMP_WITH_TIME_POS, TEMP_FONT, mTemperature, UNIT_FONT, unit);
377 } else {
378 image->drawComposedString(TEMP_POS, TEMP_FONT, mTemperature, UNIT_FONT, unit);
380 image->setWindowPixmap(mIconWin);
381 delete image;
382 XSync(mDisplay, False);
383 return true;
388 std::cerr << APPNAME << ": could not fetch temperature (unknown file format)" << std::endl;
389 return false;
392 void Temperature::run()
394 if (mShowTime) {
395 calcTimeDiff();
398 int counter = 0;
399 while(1) {
400 if (counter <= 0) {
401 char tmpFile[sizeof(TMP_FILE)] = TMP_FILE;
402 int fd = mkstemp(tmpFile);
403 if (fd == -1) {
404 std::cerr << APPNAME << ": could not create temporary file " << tmpFile << ": " << strerror(errno) << std::endl;
405 exit(1);
407 close(fd);
409 counter = UPDATE_INTERVAL;
410 childStatus = ChildRunning;
411 signal(SIGCHLD, catchChildExit);
412 showLed(true);
413 int pid = fork();
414 if (pid == 0) {
415 const char* verbose = (mVerbose ? "--verbose" : "--quiet");
416 char* URL = new char[strlen(METAR_URL) + strlen(mStationId) + 1];
417 sprintf(URL, METAR_URL, mStationId);
418 execlp("wget", "wget", "--cache=off", "--tries=0", verbose, "-O", tmpFile, URL, 0);
419 std::cerr << APPNAME << ": could not fetch temperature (wget not found in $PATH)" << std::endl;
420 remove(tmpFile);
421 exit(0);
422 } else if (pid == -1) {
423 std::cerr << APPNAME << ": could not fetch temperature (fork() failed)" << std::endl;
424 } else {
425 bool toggle = true;
426 while (childStatus == ChildRunning) {
427 showLed(toggle);
428 toggle ^= true;
429 sleep(1);
431 showLed(true);
432 if (childStatus == ChildDone) {
433 ifstream file(tmpFile);
434 if (file) {
435 if (updateTemperture(file)) {
436 showLed(false);
438 file.close();
441 remove(tmpFile);
443 } else {
444 counter--;
445 sleep(1);
446 XSync(mDisplay, False);