1 // This file is part of Golly.
2 // See docs/License.html for the copyright notice.
3
4 #include <sys/time.h> // for gettimeofday
5 #include <algorithm> // for std::transform
6 #include <ctype.h> // for tolower
7
8 #include "lifepoll.h" // for lifepoll
9 #include "util.h" // for linereader
10
11 #include "prefs.h" // for allowbeep, tempdir
12 #include "utils.h"
13
14 #ifdef ANDROID_GUI
15 #include "jnicalls.h" // for AndroidWarning, AndroidBeep, UpdateStatus, etc
16 #endif
17
18 #ifdef WEB_GUI
19 #include "webcalls.h" // for WebWarning, WebBeep, UpdateStatus, etc
20 #endif
21
22 #ifdef IOS_GUI
23 #import <AudioToolbox/AudioToolbox.h> // for AudioServicesPlaySystemSound, etc
24 #import "PatternViewController.h" // for UpdateStatus, etc
25 #endif
26
27 // -----------------------------------------------------------------------------
28
29 int event_checker = 0; // if > 0 then we're in gollypoller.checkevents()
30
31 // -----------------------------------------------------------------------------
32
SetColor(gColor & color,unsigned char red,unsigned char green,unsigned char blue)33 void SetColor(gColor& color, unsigned char red, unsigned char green, unsigned char blue)
34 {
35 color.r = red;
36 color.g = green;
37 color.b = blue;
38 }
39
40 // -----------------------------------------------------------------------------
41
SetRect(gRect & rect,int x,int y,int width,int height)42 void SetRect(gRect& rect, int x, int y, int width, int height)
43 {
44 rect.x = x;
45 rect.y = y;
46 rect.width = width;
47 rect.height = height;
48 }
49
50 // -----------------------------------------------------------------------------
51
52 #ifdef IOS_GUI
53
54 // need the following to make YesNo/Warning/Fatal dialogs modal:
55
56 @interface ModalAlertDelegate : NSObject <UIAlertViewDelegate>
57 {
58 NSInteger returnButt;
59 }
60
61 @property () NSInteger returnButt;
62
63 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
64
65 @end
66
67 @implementation ModalAlertDelegate
68
69 @synthesize returnButt;
70
71 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
72 {
73 returnButt = buttonIndex;
74 }
75
76 @end
77
78 #endif // IOS_GUI
79
80 // -----------------------------------------------------------------------------
81
YesNo(const char * msg)82 bool YesNo(const char* msg)
83 {
84 Beep();
85
86 #ifdef ANDROID_GUI
87 return AndroidYesNo(msg);
88 #endif
89
90 #ifdef WEB_GUI
91 return WebYesNo(msg);
92 #endif
93
94 #ifdef IOS_GUI
95 ModalAlertDelegate *md = [[ModalAlertDelegate alloc] init];
96 md.returnButt = -1;
97
98 UIAlertView *a = [[UIAlertView alloc] initWithTitle:@"Warning"
99 message:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]
100 delegate:md
101 cancelButtonTitle:@"No"
102 otherButtonTitles:@"Yes", nil];
103 [a show];
104
105 // wait for user to hit button
106 while (md.returnButt == -1) {
107 event_checker++;
108 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
109 event_checker--;
110 }
111
112 return md.returnButt != 0;
113 #endif // IOS_GUI
114 }
115
116 // -----------------------------------------------------------------------------
117
Warning(const char * msg)118 void Warning(const char* msg)
119 {
120 Beep();
121
122 #ifdef ANDROID_GUI
123 AndroidWarning(msg);
124 #endif
125
126 #ifdef WEB_GUI
127 WebWarning(msg);
128 #endif
129
130 #ifdef IOS_GUI
131 ModalAlertDelegate *md = [[ModalAlertDelegate alloc] init];
132 md.returnButt = -1;
133
134 UIAlertView *a = [[UIAlertView alloc] initWithTitle:@"Warning"
135 message:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]
136 delegate:md
137 cancelButtonTitle:@"OK"
138 otherButtonTitles:nil];
139 [a show];
140
141 // wait for user to hit button
142 while (md.returnButt == -1) {
143 event_checker++;
144 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
145 event_checker--;
146 }
147 #endif // IOS_GUI
148 }
149
150 // -----------------------------------------------------------------------------
151
Fatal(const char * msg)152 void Fatal(const char* msg)
153 {
154 Beep();
155
156 #ifdef ANDROID_GUI
157 AndroidFatal(msg);
158 #endif
159
160 #ifdef WEB_GUI
161 WebFatal(msg);
162 #endif
163
164 #ifdef IOS_GUI
165 ModalAlertDelegate *md = [[ModalAlertDelegate alloc] init];
166 md.returnButt = -1;
167
168 UIAlertView *a = [[UIAlertView alloc] initWithTitle:@"Fatal Error"
169 message:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]
170 delegate:md
171 cancelButtonTitle:@"Quit"
172 otherButtonTitles:nil];
173 [a show];
174
175 // wait for user to hit button
176 while (md.returnButt == -1) {
177 event_checker++;
178 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
179 event_checker--;
180 }
181
182 exit(1);
183 #endif // IOS_GUI
184 }
185
186 // -----------------------------------------------------------------------------
187
Beep()188 void Beep()
189 {
190 if (!allowbeep) return;
191
192 #ifdef ANDROID_GUI
193 AndroidBeep();
194 #endif
195
196 #ifdef WEB_GUI
197 WebBeep();
198 #endif
199
200 #ifdef IOS_GUI
201 static SystemSoundID beepID = 0;
202 if (beepID == 0) {
203 // get the path to the sound file
204 NSString* path = [[NSBundle mainBundle] pathForResource:@"beep" ofType:@"aiff"];
205 if (path) {
206 NSURL* url = [NSURL fileURLWithPath:path];
207 OSStatus err = AudioServicesCreateSystemSoundID((__bridge CFURLRef)url, &beepID);
208 if (err == kAudioServicesNoError && beepID > 0) {
209 // play the sound
210 AudioServicesPlaySystemSound(beepID);
211 }
212 }
213 } else {
214 // assume we got the sound
215 AudioServicesPlaySystemSound(beepID);
216 }
217 #endif // IOS_GUI
218 }
219
220 // -----------------------------------------------------------------------------
221
TimeInSeconds()222 double TimeInSeconds()
223 {
224 struct timeval trec;
225 gettimeofday(&trec, 0);
226 return double(trec.tv_sec) + double(trec.tv_usec) / 1.0e6;
227 }
228
229 // -----------------------------------------------------------------------------
230
CreateTempFileName(const char * prefix)231 std::string CreateTempFileName(const char* prefix)
232 {
233 /*
234 std::string tmplate = tempdir;
235 tmplate += prefix;
236 tmplate += ".XXXXXX";
237 std::string path = mktemp((char*)tmplate.c_str());
238 */
239
240 // simpler to ignore prefix and create /tmp/0, /tmp/1, /tmp/2, etc
241 char n[32];
242 static int nextname = 0;
243 sprintf(n, "%d", nextname++);
244 std::string path = tempdir + n;
245
246 return path;
247 }
248
249 // -----------------------------------------------------------------------------
250
FileExists(const std::string & filepath)251 bool FileExists(const std::string& filepath)
252 {
253 FILE* f = fopen(filepath.c_str(), "r");
254 if (f) {
255 fclose(f);
256 return true;
257 } else {
258 return false;
259 }
260 }
261
262 // -----------------------------------------------------------------------------
263
RemoveFile(const std::string & filepath)264 void RemoveFile(const std::string& filepath)
265 {
266 #ifdef ANDROID_GUI
267 AndroidRemoveFile(filepath);
268 #endif
269
270 #ifdef WEB_GUI
271 WebRemoveFile(filepath);
272 #endif
273
274 #ifdef IOS_GUI
275 if ([[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithCString:filepath.c_str() encoding:NSUTF8StringEncoding]
276 error:NULL] == NO) {
277 // should never happen
278 Warning("RemoveFile failed!");
279 };
280 #endif
281 }
282
283 // -----------------------------------------------------------------------------
284
CopyFile(const std::string & inpath,const std::string & outpath)285 bool CopyFile(const std::string& inpath, const std::string& outpath)
286 {
287 #if defined(ANDROID_GUI) || defined(WEB_GUI)
288 FILE* infile = fopen(inpath.c_str(), "r");
289 if (infile) {
290 // read entire file into contents
291 std::string contents;
292 const int MAXLINELEN = 4095;
293 char linebuf[MAXLINELEN + 1];
294 linereader reader(infile);
295 while (true) {
296 if (reader.fgets(linebuf, MAXLINELEN) == 0) break;
297 contents += linebuf;
298 contents += "\n";
299 }
300 reader.close();
301 // fclose(infile) has been called
302
303 // write contents to outpath
304 FILE* outfile = fopen(outpath.c_str(), "w");
305 if (outfile) {
306 if (fputs(contents.c_str(), outfile) == EOF) {
307 fclose(outfile);
308 Warning("CopyFile failed to copy contents to output file!");
309 return false;
310 }
311 fclose(outfile);
312 } else {
313 Warning("CopyFile failed to open output file!");
314 return false;
315 }
316
317 return true;
318 } else {
319 Warning("CopyFile failed to open input file!");
320 return false;
321 }
322 #endif // ANDROID_GUI or WEB_GUI
323
324 #ifdef IOS_GUI
325 if (FileExists(outpath)) {
326 RemoveFile(outpath);
327 }
328 return [[NSFileManager defaultManager] copyItemAtPath:[NSString stringWithCString:inpath.c_str() encoding:NSUTF8StringEncoding]
329 toPath:[NSString stringWithCString:outpath.c_str() encoding:NSUTF8StringEncoding]
330 error:NULL];
331 #endif // IOS_GUI
332 }
333
334 // -----------------------------------------------------------------------------
335
MoveFile(const std::string & inpath,const std::string & outpath)336 bool MoveFile(const std::string& inpath, const std::string& outpath)
337 {
338 #ifdef ANDROID_GUI
339 return AndroidMoveFile(inpath, outpath);
340 #endif
341
342 #ifdef WEB_GUI
343 return WebMoveFile(inpath, outpath);
344 #endif
345
346 #ifdef IOS_GUI
347 if (FileExists(outpath)) {
348 RemoveFile(outpath);
349 }
350 return [[NSFileManager defaultManager] moveItemAtPath:[NSString stringWithCString:inpath.c_str() encoding:NSUTF8StringEncoding]
351 toPath:[NSString stringWithCString:outpath.c_str() encoding:NSUTF8StringEncoding]
352 error:NULL];
353 #endif
354 }
355
356 // -----------------------------------------------------------------------------
357
FixURLPath(std::string & path)358 void FixURLPath(std::string& path)
359 {
360 // replace "%..." with suitable chars for a file path (eg. %20 is changed to space)
361
362 #ifdef ANDROID_GUI
363 AndroidFixURLPath(path);
364 #endif
365
366 #ifdef WEB_GUI
367 WebFixURLPath(path);
368 #endif
369
370 #ifdef IOS_GUI
371 NSString* newpath = [[NSString stringWithCString:path.c_str() encoding:NSUTF8StringEncoding]
372 stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
373 if (newpath) path = [newpath cStringUsingEncoding:NSUTF8StringEncoding];
374 #endif
375 }
376
377 // -----------------------------------------------------------------------------
378
IsHTMLFile(const std::string & filename)379 bool IsHTMLFile(const std::string& filename)
380 {
381 size_t dotpos = filename.rfind('.');
382 if (dotpos == std::string::npos) return false;
383
384 std::string ext = filename.substr(dotpos+1);
385 return ( strcasecmp(ext.c_str(),"htm") == 0 ||
386 strcasecmp(ext.c_str(),"html") == 0 );
387 }
388
389 // -----------------------------------------------------------------------------
390
IsTextFile(const std::string & filename)391 bool IsTextFile(const std::string& filename)
392 {
393 if (!IsHTMLFile(filename)) {
394 // if non-html file name contains "readme" then assume it's a text file
395 std::string basename = filename;
396 size_t lastsep = basename.rfind('/');
397 if (lastsep != std::string::npos) {
398 basename = basename.substr(lastsep+1);
399 }
400 std::transform(basename.begin(), basename.end(), basename.begin(), tolower);
401 if (basename.find("readme") != std::string::npos) return true;
402 }
403 size_t dotpos = filename.rfind('.');
404 if (dotpos == std::string::npos) return false;
405
406 std::string ext = filename.substr(dotpos+1);
407 return ( strcasecmp(ext.c_str(),"txt") == 0 ||
408 strcasecmp(ext.c_str(),"doc") == 0 );
409 }
410
411 // -----------------------------------------------------------------------------
412
IsZipFile(const std::string & filename)413 bool IsZipFile(const std::string& filename)
414 {
415 size_t dotpos = filename.rfind('.');
416 if (dotpos == std::string::npos) return false;
417
418 std::string ext = filename.substr(dotpos+1);
419 return ( strcasecmp(ext.c_str(),"zip") == 0 ||
420 strcasecmp(ext.c_str(),"gar") == 0 );
421 }
422
423 // -----------------------------------------------------------------------------
424
IsRuleFile(const std::string & filename)425 bool IsRuleFile(const std::string& filename)
426 {
427 size_t dotpos = filename.rfind('.');
428 if (dotpos == std::string::npos) return false;
429
430 std::string ext = filename.substr(dotpos+1);
431 return ( strcasecmp(ext.c_str(),"rule") == 0 ||
432 strcasecmp(ext.c_str(),"table") == 0 ||
433 strcasecmp(ext.c_str(),"tree") == 0 ||
434 strcasecmp(ext.c_str(),"colors") == 0 ||
435 strcasecmp(ext.c_str(),"icons") == 0 );
436 }
437
438 // -----------------------------------------------------------------------------
439
IsScriptFile(const std::string & filename)440 bool IsScriptFile(const std::string& filename)
441 {
442 size_t dotpos = filename.rfind('.');
443 if (dotpos == std::string::npos) return false;
444
445 std::string ext = filename.substr(dotpos+1);
446 return ( strcasecmp(ext.c_str(),"lua") == 0 ||
447 strcasecmp(ext.c_str(),"pl") == 0 ||
448 strcasecmp(ext.c_str(),"py") == 0 );
449 }
450
451 // -----------------------------------------------------------------------------
452
EndsWith(const std::string & str,const std::string & suffix)453 bool EndsWith(const std::string& str, const std::string& suffix)
454 {
455 // return true if str ends with suffix
456 size_t strlen = str.length();
457 size_t sufflen = suffix.length();
458 return (strlen >= sufflen) && (str.rfind(suffix) == strlen - sufflen);
459 }
460
461 // -----------------------------------------------------------------------------
462
463 // let gollybase modules process events
464
465 class golly_poll : public lifepoll
466 {
467 public:
468 virtual int checkevents();
469 virtual void updatePop();
470 };
471
checkevents()472 int golly_poll::checkevents()
473 {
474 if (event_checker > 0) return isInterrupted();
475 event_checker++;
476
477 #ifdef ANDROID_GUI
478 AndroidCheckEvents();
479 #endif
480
481 #ifdef WEB_GUI
482 WebCheckEvents();
483 #endif
484
485 #ifdef IOS_GUI
486 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
487 #endif
488
489 event_checker--;
490 return isInterrupted();
491 }
492
updatePop()493 void golly_poll::updatePop()
494 {
495 UpdateStatus();
496 }
497
498 // -----------------------------------------------------------------------------
499
500 golly_poll gollypoller; // create instance
501
Poller()502 lifepoll* Poller()
503 {
504 return &gollypoller;
505 }
506
PollerReset()507 void PollerReset()
508 {
509 gollypoller.resetInterrupted();
510 }
511
PollerInterrupt()512 void PollerInterrupt()
513 {
514 gollypoller.setInterrupted();
515 }
516