1 /*
2 
3     eboard - chess client
4     http://www.bergo.eng.br/eboard
5     https://github.com/fbergo/eboard
6     Copyright (C) 2000-2016 Felipe Bergo
7     fbergo/at/gmail/dot/com
8 
9     This program is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation; either version 2 of the License, or
12     (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 
23 */
24 
25 #include <iostream>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <dirent.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/wait.h>
35 #include <time.h>
36 #include <signal.h>
37 #include <dlfcn.h>
38 
39 #define GLOBAL_CC 1
40 
41 #include "global.h"
42 #include "text.h"
43 #include "config.h"
44 
45 #include "chess.h"
46 #include "tstring.h"
47 #include "notebook.h"
48 #include "board.h"
49 #include "quickbar.h"
50 #include "pieces.h"
51 #include "eboard.h"
52 
53 
54 /* Global variables needed to cleanly close the driver */
55 extern int (*dgtnixClose)();
56 extern bool DgtInit;
57 extern void *dgtnix_dll_handle;
58 Global global;
59 
60 // stream ops
61 
operator <<(ostream & s,WindowGeometry w)62 ostream & operator<<(ostream &s, WindowGeometry w) {
63   s << '(' << w.X << ',' << w.Y << ',';
64   s << w.W << ',' << w.H << ')';
65   return(s);
66 }
67 
operator <<(ostream & s,Desktop d)68 ostream & operator<<(ostream &s, Desktop d) {
69   s << d.wMain << ',' << d.wGames << ',' << d.wLocal << ',';
70   s << d.wAds << ',' << d.PanePosition                          ;
71   return(s);
72 }
73 
operator <<(ostream & s,TimeControl tc)74 ostream & operator<<(ostream &s, TimeControl tc) {
75   TimeControl x;
76   x = tc;
77   if (x.mode == TC_SPM) x.value[1] = 0;
78   if (x.mode == TC_NONE) x.value[0] = x.value[1] = 0;
79   s << '/' << ((int)(x.mode)) << '/' << x.value[0];
80   s << '/' << x.value[1];
81   return(s);
82 }
83 
operator <<(ostream & s,EngineBookmark b)84 ostream & operator<<(ostream &s, EngineBookmark b) {
85   s << b.caption << '^';
86   if (b.directory.empty()) s << "NULL"; else s << b.directory;
87   s << '^';
88   if (b.cmdline.empty()) s << "NULL"; else s << b.cmdline;
89   s << '^' << b.humanwhite << '^' << b.timecontrol << '^';
90   s << b.maxply  << '^' << b.think << '^';
91   s << b.proto << '^' << ((int)b.mode);
92   return(s);
93 }
94 
operator <<(ostream & s,TerminalColor t)95 ostream & operator<<(ostream &s, TerminalColor t) {
96   s.setf(ios::hex,ios::basefield);
97   s << t.TextDefault    << ',';
98   s << t.TextBright     << ',';
99   s << t.PrivateTell    << ',';
100   s << t.NewsNotify     << ',';
101   s << t.Mamer          << ',';
102   s << t.KibitzWhisper  << ',';
103   s << t.Shouts         << ',';
104   s << t.Seeks          << ',';
105   s << t.ChannelTell    << ',';
106   s << t.Engine         << ',';
107   s << t.Background;
108   s.setf(ios::dec,ios::basefield);
109   return(s);
110 }
111 
Global()112 Global::Global() {
113   int i;
114 
115   input=0;
116   output=0;
117   network=0;
118   status=0;
119   protocol=0;
120   chandler=0;
121   promotion=0;
122   ebook=0;
123   skgraph2=0;
124   inputhistory=0;
125   bmlistener=0;
126   qbcontainer=0;
127   quickbar=0;
128   killbox=0;
129   lowernotebook=0;
130   mainpaned=0;
131   bugpane=0;
132   pieceset=0;
133   toplevelwidget=0;
134 
135   LastScratch = 0;
136 
137   Quitting=0;
138   Version=VERSION;
139   SelfInputColor = 0xc0c0ff;
140 
141   HilightLastMove=0;
142   AnimateMoves=0;
143   Premove=1;
144 
145   PasswordMode = 0;
146 
147   TabPos=0;
148 
149   CommLog=0;
150   DebugLog=0;
151   PauseLog=0;
152 
153   MainLevel=0;
154   QuitPending=0;
155 
156   ScrollBack=1000;
157   FicsAutoLogin=1;
158   BeepWhenOppMoves=0;
159   EnableSounds=0;
160   PopupSecondaryGames=1;
161   SmartDiscard=0;
162   ShowCoordinates=0;
163 
164   PlainSquares=0;
165   LightSqColor=0xe7cf93;
166   DarkSqColor=0x9e8661;
167 
168   ShowTimestamp = 0;
169   ShowRating=1;
170   SpecialChars = 3;
171   MsecThreshold = 60;
172   MsecDigits = 3;
173 
174   UseVectorPieces=0;
175   CheckLegality=0;
176   DrawHouseStock=1;
177 
178   AppendPlayed=0;
179   AppendObserved=0;
180   strcpy(AppendFile,"~/.eboard/mygames.pgn");
181 
182   IcsSeekGraph=1;
183   HideSeeks=0;
184 
185   SplitChannels=0;
186   ChannelsToConsoleToo=0;
187 
188   SmootherAnimation=0;
189 
190   // chess machine had this and FICS admins didn't like it
191   // if you turn it on, you may be committing abuse.
192   // I'm not going to delete the code. I'm just removing the
193   // user-friendly controls from the config dialog and setting
194   // the defaults to safe values.
195 
196   IcsAllObPlayed   = 0;
197   IcsAllObObserved = 0;
198 
199   ShowQuickbar=1;
200   LowTimeWarningLimit=5;
201   RetrieveChannelNames=1;
202 
203   sndevents[1].Pitch=650;
204   sndevents[1].Duration=350;
205 
206   sndevents[2].Pitch=900;
207   sndevents[2].Duration=80;
208 
209   sndevents[3].Pitch=444;
210   sndevents[3].Duration=40;
211   sndevents[3].Count=3;
212 
213   sndevents[4].Pitch=740;
214   sndevents[4].Duration=120;
215 
216   for(i=5;i<N_SOUND_EVENTS;i++) {
217     sndevents[i].Pitch=610;
218     sndevents[i].Duration=60;
219     sndevents[i].enabled=false;
220   }
221 
222   strcpy(ClockFont,DEFAULT_FONT_CLOK);
223   strcpy(PlayerFont,DEFAULT_FONT_PLYR);
224   strcpy(InfoFont,DEFAULT_FONT_INFO);
225   strcpy(ConsoleFont,DEFAULT_FONT_CONS);
226   strcpy(SeekFont,DEFAULT_FONT_SEEK);
227 
228   memset(P2PName,0,64);
229   env.User.copy(P2PName,63);
230 
231   RCKeys.push_back("HilightLastMove"); //0
232   RCKeys.push_back("AnimateMoves");
233   RCKeys.push_back("Premove");         //2
234   RCKeys.push_back("PieceSet");
235   RCKeys.push_back("TabPos");          //4
236   RCKeys.push_back("ClockFont");
237   RCKeys.push_back("PlayerFont");      //6
238   RCKeys.push_back("InfoFont");
239   RCKeys.push_back("PlainSquares");    //8
240   RCKeys.push_back("LightSqColor");
241   RCKeys.push_back("DarkSqColor");     //10
242   RCKeys.push_back("Host");
243   RCKeys.push_back("Antialias");       //12 [deprecated]
244   RCKeys.push_back("ShowRating");
245   RCKeys.push_back("ScrollBack");      //14
246   RCKeys.push_back("FicsAutoLogin");
247   RCKeys.push_back("BeepOpp");         //16
248   RCKeys.push_back("SoundEvent");
249   RCKeys.push_back("EnableSounds");    //18
250   RCKeys.push_back("VectorPieces");
251   RCKeys.push_back("CheckLegality");   //20
252   RCKeys.push_back("AppendPlayed");
253   RCKeys.push_back("AppendObserved");  //22
254   RCKeys.push_back("AppendFile");
255   RCKeys.push_back("ConsoleFont");     //24
256   RCKeys.push_back("SeekGraph");
257   RCKeys.push_back("HideSeeks");       //26
258   RCKeys.push_back("SplitChannels");
259   RCKeys.push_back("ChSplitAndCons");  //28
260   RCKeys.push_back("DrawHouseStock");
261   RCKeys.push_back("SquareSet");       //30
262   RCKeys.push_back("PopupSecondaryGames");
263   RCKeys.push_back("SmartDiscard");    //32
264   RCKeys.push_back("ShowCoordinates");
265   RCKeys.push_back("TerminalColors");  //34
266   RCKeys.push_back("DesktopState");
267   RCKeys.push_back("DesktopStateDC");  //36
268   RCKeys.push_back("ShowQuickbar");
269   RCKeys.push_back("QuickbarButton");  //38
270   RCKeys.push_back("LowTimeWarningLimit");
271   RCKeys.push_back("RetrieveChannelNames"); //40
272   RCKeys.push_back("SmootherAnimation");
273   RCKeys.push_back("EngineBookmark");  // 42
274   RCKeys.push_back("SeekFont");
275   RCKeys.push_back("IcsAllObPlayed");  // 44
276   RCKeys.push_back("IcsAllObObserved");
277   RCKeys.push_back("P2PName");         // 46
278   RCKeys.push_back("ShowTimestamp");
279   RCKeys.push_back("SpecialChars");// 48
280   RCKeys.push_back("JSCAxis"); //          [deprecated]
281   RCKeys.push_back("JSBAxis"); // 50       [deprecated]
282   RCKeys.push_back("JSMButton"); //        [deprecated]
283   RCKeys.push_back("JSNTButton"); // 52 // [deprecated]
284   RCKeys.push_back("JSPTButton"); //       [deprecated]
285   RCKeys.push_back("JSMode"); // 54 //     [deprecated]
286   RCKeys.push_back("JSSpeed"); //          [deprecated]
287   RCKeys.push_back("MsecThreshold"); // 56
288   RCKeys.push_back("MsecDigits");
289 
290   PopupHelp = false;
291 }
292 
dropQuickbarButtons()293 void Global::dropQuickbarButtons() {
294   unsigned int i;
295   for(i=0;i<QuickbarButtons.size();i++)
296     delete(QuickbarButtons[i]);
297   QuickbarButtons.clear();
298 }
299 
clearDupes(ChessGame * cg)300 void Global::clearDupes(ChessGame *cg) {
301   list<ChessGame *>::iterator gi;
302   for(gi=GameList.begin();gi!=GameList.end();gi++)
303     if ( (*(*gi)) == cg->GameNumber )
304       renumberGame(*gi,nextFreeGameId(8000));
305 }
306 
deleteGame(ChessGame * cg)307 void Global::deleteGame(ChessGame *cg) {
308   list<ChessGame *>::iterator gi;
309   for(gi=GameList.begin();gi!=GameList.end();gi++)
310     if ( (*gi) == cg ) {
311       GameList.erase(gi);
312       return;
313     }
314 }
315 
appendGame(ChessGame * cg,bool RenumberDupes)316 void Global::appendGame(ChessGame *cg,bool RenumberDupes) {
317   if (RenumberDupes) clearDupes(cg);
318   GameList.push_back(cg);
319 }
prependGame(ChessGame * cg,bool RenumberDupes)320 void Global::prependGame(ChessGame *cg, bool RenumberDupes) {
321   if (RenumberDupes) clearDupes(cg);
322   GameList.push_front(cg);
323 }
324 
renumberGame(ChessGame * cg,int id)325 void Global::renumberGame(ChessGame *cg,int id) {
326   int oldid;
327   oldid=cg->GameNumber;
328   cg->GameNumber=id;
329   // renumber notebook references
330   if (ebook) ebook->renumberPage(oldid,id);
331 }
332 
removeBoard(Board * b)333 void Global::removeBoard(Board *b) {
334   for(BLi=BoardList.begin();BLi!=BoardList.end();BLi++)
335     if ( (*BLi) == b ) {
336       BoardList.erase(BLi);
337       return;
338     }
339   //  cerr << "<Global::removeBoard> ** board not found\n";
340 }
341 
effectiveLegalityChecking()342 bool Global::effectiveLegalityChecking() {
343   if (CheckLegality) return true;
344 
345   if (protocol != NULL)
346     return(protocol->requiresLegalityChecking());
347 
348   return false;
349 }
350 
statOS()351 void Global::statOS() {
352   FILE *p;
353   p=popen("uname -s","r");
354   if (!p) p=popen("/bin/uname -s","r");
355   if (!p) p=popen("/sbin/uname -s","r");
356   if (!p) p=popen("/usr/bin/uname -s","r");
357   if (!p) p=popen("/usr/sbin/uname -s","r");
358   if (!p) { strcpy(SystemType,"unknown"); return; }
359   SystemType[63]=0;
360   fgets(SystemType,64,p);
361   pclose(p);
362   if (SystemType[strlen(SystemType)-1]=='\n')
363     SystemType[strlen(SystemType)-1]=0;
364 }
365 
ensureDirectories()366 void Global::ensureDirectories() {
367   char z[256];
368   DIR *tdir;
369 
370   if (env.Home.empty()) {
371     cerr << _("[eboard] ** no $HOME") << endl;
372     return;
373   } else if (strlen(env.Home.c_str()) > 230) {
374     cerr << _("[eboard] ** $HOME is too long") << endl;
375     return;
376   }
377   snprintf(z,256,"%s/.eboard",env.Home.c_str());
378 
379   tdir=opendir(z);
380   if (tdir==NULL)
381     PopupHelp = true;
382   else
383     closedir(tdir);
384 
385   if (createDir(z)) return;
386   snprintf(z,256,"%s/.eboard/craftylog",env.Home.c_str());
387   createDir(z);
388   snprintf(z,256,"%s/.eboard/eng-out",env.Home.c_str());
389   createDir(z);
390   snprintf(z,256,"%s/.eboard/scripts",env.Home.c_str());
391   createDir(z);
392 }
393 
createDir(char * z)394 int Global::createDir(char *z) {
395   DIR *tdir;
396   tdir=opendir(z);
397   if (tdir)
398     closedir(tdir);
399   else
400     if (mkdir(z,0755)) {
401       cerr << _("[eboard] ** failed to create directory ") << z << endl;
402       return -1;
403     }
404   return 0;
405 }
406 
readRC()407 void Global::readRC() {
408   tstring t;
409   string  *p;
410   char    line[512],rev[128];
411 
412   static const char *sep=" \n\t,:\r^";
413   static const char *sep2="\n\t,:\r";
414   static const char *sep3=" \n\t:\r";
415   static const char *sep4=",\n\r";
416   static const char *sep5="\n\t:\r";
417 
418   HostBookmark *hbm;
419   EngineBookmark *ebm;
420   int i,j;
421   QButton *qb;
422 
423   if (env.Config.empty())
424     return;
425 
426   ifstream rc(env.Config.c_str());
427 
428   if (!rc)
429     return;
430 
431   memset(rev,0,128);
432   rev['R']=0;
433   rev['L']=1;
434   rev['T']=2;
435   rev['B']=3;
436 
437   t.setChomp(true);
438 
439   memset(line,0,512);
440   while(rc.getline(line,511,'\n')) {
441     t.set(line);
442     memset(line,0,512);
443 
444     p=t.token(sep);
445     if (!p) continue;
446     if (p->at(0)=='#') continue;
447 
448     for(j=0;j<=57;j++) {
449       if (! p->compare(RCKeys[j]) ) {
450 	switch(j) {
451 	case  0: HilightLastMove =t.tokenvalue(sep); break;
452 	case  1: AnimateMoves    =t.tokenvalue(sep); break;
453 	case  2: Premove         =t.tokenvalue(sep); break;
454 	case  3: setPieceSet(*(t.token(sep3)),true,true); break;
455 	case  4: p=t.token(sep); TabPos=rev[p->at(0)]; break;
456 	case  5: p=t.token(sep2);        memset(ClockFont,0,96);
457 	         p->copy(ClockFont,95);  break;
458 	case  6: p=t.token(sep2);        memset(PlayerFont,0,96);
459 	         p->copy(PlayerFont,95); break;
460 	case  7: p=t.token(sep2);        memset(InfoFont,0,96);
461 	         p->copy(InfoFont,95);   break;
462 	case  8: PlainSquares    =t.tokenvalue(sep); break;
463 	case  9: LightSqColor    =t.tokenvalue(sep,16); break;
464 	case 10: DarkSqColor     =t.tokenvalue(sep,16); break;
465 	case 11: hbm=new HostBookmark();
466 	         p=t.token(sep4); p->copy(hbm->host,128);
467 	         hbm->port=t.tokenvalue(sep4);
468 	         p=t.token(sep4); p->copy(hbm->protocol,64);
469 	         HostHistory.push_back(hbm);
470 	         break;
471 	case 12: break; // deprecated (antialias)
472 	case 13: ShowRating      =t.tokenvalue(sep); break;
473 	case 14: ScrollBack      =t.tokenvalue(sep); break;
474 	case 15: FicsAutoLogin   =t.tokenvalue(sep); break;
475 	case 16: BeepWhenOppMoves=t.tokenvalue(sep); break;
476 	case 17: i=t.tokenvalue(sep);
477 	         if (i < N_SOUND_EVENTS) sndevents[i].read(t);
478 	         break;
479 	case 18: EnableSounds    =t.tokenvalue(sep); break;
480 	case 19: UseVectorPieces =t.tokenvalue(sep); break;
481 	case 20: CheckLegality   =t.tokenvalue(sep); break;
482 	case 21: AppendPlayed    =t.tokenvalue(sep); break;
483 	case 22: AppendObserved  =t.tokenvalue(sep); break;
484 	case 23: p=t.token(sep);          memset(AppendFile,0,128);
485  	         p->copy(AppendFile,127); break;
486 	case 24: p=t.token(sep2);         memset(ConsoleFont,0,96);
487 	         p->copy(ConsoleFont,95); break;
488 	case 25: IcsSeekGraph        =t.tokenvalue(sep); break;
489 	case 26: HideSeeks           =t.tokenvalue(sep); break;
490 	case 27: SplitChannels       =t.tokenvalue(sep); break;
491 	case 28: ChannelsToConsoleToo=t.tokenvalue(sep); break;
492 	case 29: DrawHouseStock      =t.tokenvalue(sep); break;
493 	case 30: p=t.token(sep3);
494 	         if (p->compare(pieceset->getSquareName()))
495 		   setPieceSet(*p,false,true);
496 		 break;
497 	case 31: PopupSecondaryGames =t.tokenvalue(sep); break;
498 	case 32: SmartDiscard        =t.tokenvalue(sep); break;
499 	case 33: ShowCoordinates     =t.tokenvalue(sep); break;
500 	case 34: Colors.read(t);      break;
501 	case 35: Desk.read(t);        break;
502 	case 36: Desk.readConsole(t); break;
503 	case 37: ShowQuickbar        =t.tokenvalue(sep); break;
504 	case 38: qb=new QButton(); qb->icon=t.tokenvalue(sep5);
505        	         qb->caption=*(t.token(sep5)); qb->command=*(t.token(sep5));
506 		 QuickbarButtons.push_back(qb);
507 		 break;
508 	case 39: LowTimeWarningLimit =t.tokenvalue(sep); break;
509 	case 40: RetrieveChannelNames=t.tokenvalue(sep); break;
510 	case 41: SmootherAnimation   =t.tokenvalue(sep); break;
511 	case 42: ebm=new EngineBookmark(); ebm->read(t);
512 	         EnginePresets.push_back(ebm); break;
513 	case 43: p=t.token(sep2); memset(SeekFont,0,96);
514 	         p->copy(SeekFont,95); break;
515 		 //	default: cerr << "ignored [" << (*p) << "]\n";
516 	case 44: IcsAllObPlayed      =t.tokenvalue(sep); break;
517 	case 45: IcsAllObObserved    =t.tokenvalue(sep); break;
518 	case 46: p=t.token(sep2); memset(P2PName,0,64);
519                  p->copy(P2PName,63); break;
520 	case 47: ShowTimestamp       =t.tokenvalue(sep); break;
521 	case 48: SpecialChars        =t.tokenvalue(sep); break;
522 	case 49:
523 	case 50:
524 	case 51:
525 	case 52:
526 	case 53:
527 	case 54:
528 	case 55:
529 	  // 49-55: deprecated joystick-related keys
530 	  break;
531 	case 56: MsecThreshold = t.tokenvalue(sep); break;
532 	case 57: MsecDigits = t.tokenvalue(sep); break;
533 	} // switch
534       } // compare
535     } // for j 0..57
536   } // while getline
537 
538   rc.close();
539 }
540 
writeRC()541 void Global::writeRC() {
542 
543   string div;
544 
545   list<HostBookmark *>::iterator bi;
546   list<EngineBookmark *>::iterator ei;
547   static const char *tabpos="RLTB";
548   unsigned int i;
549 
550   if (env.Config.empty())
551     return;
552 
553   div="::";
554 
555   ofstream rc(env.Config.c_str());
556   if (!rc)
557     return;
558 
559   rc << RCKeys[0] << div << HilightLastMove << endl;
560   rc << RCKeys[1] << div << AnimateMoves << endl;
561   rc << RCKeys[2] << div << Premove << endl;
562   rc << RCKeys[3] << div << pieceset->getName() << endl;
563   rc << RCKeys[4] << div << tabpos[TabPos%4] << endl;
564   rc << RCKeys[5] << div << ClockFont << endl;
565   rc << RCKeys[6] << div << PlayerFont << endl;
566   rc << RCKeys[7] << div << InfoFont << endl;
567   rc << RCKeys[8] << div << PlainSquares << endl;
568 
569   rc.setf(ios::hex, ios::basefield);
570   rc << RCKeys[9]  << div << LightSqColor << endl;
571   rc << RCKeys[10] << div << DarkSqColor << endl;
572   rc.setf(ios::dec, ios::basefield);
573 
574   for(bi=HostHistory.begin();bi!=HostHistory.end();bi++)
575     rc << RCKeys[11] << ',' << (*bi)->host << ',' <<
576       (*bi)->port << ',' << (*bi)->protocol << endl;
577 
578   // 12: antialias deprecated
579   rc << RCKeys[13] << div << ShowRating << endl;
580   rc << RCKeys[14] << div << ScrollBack << endl;
581   rc << RCKeys[15] << div << FicsAutoLogin<< endl;
582   rc << RCKeys[16] << div << BeepWhenOppMoves << endl;
583 
584   for(i=0;i< N_SOUND_EVENTS;i++)
585     rc << RCKeys[17] << div << i << ',' << sndevents[i] << endl;
586 
587   rc << RCKeys[18] << div << EnableSounds << endl;
588   rc << RCKeys[19] << div << UseVectorPieces << endl;
589   rc << RCKeys[20] << div << CheckLegality << endl;
590   rc << RCKeys[21] << div << AppendPlayed << endl;
591   rc << RCKeys[22] << div << AppendObserved << endl;
592   rc << RCKeys[23] << div << AppendFile << endl;
593 
594   rc << RCKeys[24] << div << ConsoleFont << endl;
595   rc << RCKeys[25] << div << IcsSeekGraph << endl;
596   rc << RCKeys[26] << div << HideSeeks << endl;
597   rc << RCKeys[27] << div << SplitChannels << endl;
598   rc << RCKeys[28] << div << ChannelsToConsoleToo << endl;
599   rc << RCKeys[29] << div << DrawHouseStock << endl;
600   rc << RCKeys[30] << div << pieceset->getSquareName() << endl;
601   rc << RCKeys[31] << div << PopupSecondaryGames << endl;
602   rc << RCKeys[32] << div << SmartDiscard << endl;
603   rc << RCKeys[33] << div << ShowCoordinates << endl;
604 
605   rc << RCKeys[34] << div << Colors << endl;
606   rc << RCKeys[35] << div << Desk << endl;
607 
608   Desk.writeConsoles(rc,RCKeys[36]);
609 
610   rc << RCKeys[37] << div << ShowQuickbar << endl;
611 
612   for(i=0;i<QuickbarButtons.size();i++)
613     rc << RCKeys[38] << div << (*QuickbarButtons[i]) << endl;
614 
615   rc << RCKeys[39] << div << LowTimeWarningLimit << endl;
616   rc << RCKeys[40] << div << RetrieveChannelNames << endl;
617   rc << RCKeys[41] << div << SmootherAnimation << endl;
618 
619   for(ei=EnginePresets.begin();ei!=EnginePresets.end();ei++)
620     rc << RCKeys[42] << '^' << (*(*ei)) << endl;
621 
622   rc << RCKeys[43] << div << SeekFont << endl;
623 
624   rc << RCKeys[44] << div << IcsAllObPlayed << endl;
625   rc << RCKeys[45] << div << IcsAllObObserved << endl;
626   rc << RCKeys[46] << div << P2PName << endl;
627   rc << RCKeys[47] << div << ShowTimestamp << endl;
628   rc << RCKeys[48] << div << SpecialChars << endl;
629 
630   // 49-55: deprecated (joystick)
631 
632   rc << RCKeys[56] << div << MsecThreshold << endl;
633   rc << RCKeys[57] << div << MsecDigits << endl;
634 
635   rc.close();
636 }
637 
getGame(int num)638 ChessGame * Global::getGame(int num) {
639   list<ChessGame *>::iterator gi;
640   for(gi=GameList.begin();gi!=GameList.end();gi++)
641     if ( (*(*gi)) == num )
642       return(*gi);
643   return NULL;
644 }
645 
nextFreeGameId(int base)646 int Global::nextFreeGameId(int base) {
647   int v;
648   for(v=base;getGame(v)!=0;v++) ;
649   return v;
650 }
651 
WrappedMainIteration()652 void Global::WrappedMainIteration() {
653   MainLevel++;
654   gtk_main_iteration();
655   MainLevel--;
656   if ((!MainLevel)&&(QuitPending))
657     Global::WrappedMainQuit();
658 }
659 
WrappedMainQuit()660 void Global::WrappedMainQuit() {
661   if (MainLevel) {
662     QuitPending++;
663     return;
664   }
665   QuitPending=0;
666   signal(SIGCHLD,SIG_DFL); // prevent the crash reported by gcp
667   /* close dgtnix driver and dll */
668   if(DgtInit)
669     {
670       dgtnixClose();
671       dlclose(dgtnix_dll_handle);
672     }
673   gtk_main_quit();
674 }
675 
addAgent(NetConnection * ag)676 void Global::addAgent(NetConnection *ag) {
677   Agents.push_back(ag);
678   ag->notifyReadReady(iowatcher);
679 }
680 
removeAgent(NetConnection * ag)681 void Global::removeAgent(NetConnection *ag) {
682   list<NetConnection *>::iterator ni;
683   for(ni=Agents.begin();ni!=Agents.end();ni++)
684     if ( (*ni) == ag ) {
685       Agents.erase(ni);
686       return;
687     }
688 }
689 
agentBroadcast(char * z)690 void Global::agentBroadcast(char *z) {
691   list<NetConnection *>::iterator ni;
692   if (Agents.empty())
693     return;
694   for(ni=Agents.begin();ni!=Agents.end();ni++)
695     if ((*ni)->isConnected())
696       (*ni)->writeLine(z);
697 }
698 
receiveAgentLine(char * dest,int limit)699 int  Global::receiveAgentLine(char *dest,int limit) {
700   list<NetConnection *>::iterator ni;
701   global.debug("Global","receiveAgentLine");
702   if (Agents.empty())
703     return 0;
704   for(ni=Agents.begin();ni!=Agents.end();ni++)
705     if ( (*ni)->isConnected())
706       if ((*ni)->readLine(dest,limit)==0)
707 	return 1;
708   return 0;
709 }
710 
opponentMoved()711 void Global::opponentMoved() {
712   if (BeepWhenOppMoves && sndevents[0].enabled) {
713     if (AnimateMoves)
714       SoundStack.push(0);
715     else
716       sndevents[0].play();
717   }
718 }
719 
720 /*
721 void Global::clearSoundStack() {
722   while(!SoundStack.empty())
723     SoundStack.pop();
724 }
725 */
726 
flushSound()727 void Global::flushSound() {
728   if (!SoundStack.empty()) {
729     sndevents[SoundStack.top()].play();
730     SoundStack.pop();
731   }
732 }
733 
drawOffered()734 void Global::drawOffered()    { playOther(1); }
privatelyTold()735 void Global::privatelyTold()  { playOther(2); }
challenged()736 void Global::challenged()     { playOther(3); }
timeRunningOut()737 void Global::timeRunningOut() { playOther(4); }
gameWon()738 void Global::gameWon()        { playOther(5); }
gameLost()739 void Global::gameLost()       { playOther(6); }
gameStarted()740 void Global::gameStarted()    { playOther(7); }
gameFinished()741 void Global::gameFinished()   { playOther(8); }
742 
moveMade()743 void Global::moveMade() {
744   if (EnableSounds && sndevents[9].enabled) {
745     if (AnimateMoves)
746       SoundStack.push(9);
747     else
748       sndevents[9].play();
749   }
750 }
751 
playOther(int i)752 void Global::playOther(int i) {
753   if (i>=N_SOUND_EVENTS) return;
754   if (EnableSounds && sndevents[i].enabled)
755     sndevents[i].play();
756 }
757 
repaintAllBoards()758 void Global::repaintAllBoards() {
759   respawnPieceSet();
760 }
761 
hasSoundFile(string & p)762 bool Global::hasSoundFile(string &p) {
763   int i,j;
764   j=SoundFiles.size();
765   for(i=0;i<j;i++)
766     if ( ! SoundFiles[i].compare(p) )
767       return true;
768   return false;
769 }
770 
setPasswordMode(int pm)771 void Global::setPasswordMode(int pm) {
772   list<DetachedConsole *>::iterator i;
773   PasswordMode = pm;
774   for(i=Consoles.begin();i!=Consoles.end();i++)
775     (*i)->setPasswordMode(pm);
776 }
777 
debug(const char * klass,const char * method,const char * data)778 void Global::debug(const char *klass,const char *method,const char *data) {
779   char z[256];
780   time_t now;
781   string rm;
782 
783   if (!DebugLog)
784     return;
785 
786   if (env.Home.empty())
787     return;
788 
789   snprintf(z,256,"%s/DEBUG.eboard",env.Home.c_str());
790 
791   ofstream f(z,ios::app);
792   if (!f) return;
793 
794   rm="+ ";
795   rm+=klass;
796   rm+="::";
797   rm+=method;
798   if (data) { rm+=" ["; rm+=data; rm+=']'; }
799 
800   now=time(0);
801   strftime(z,255,"%Y-%b-%d %H:%M:%S",localtime(&now));
802 
803   f << z << " [" << ((int) getpid()) << "] " << rm << endl;
804   f.close();
805 }
806 
LogAppend(const char * msg)807 void Global::LogAppend(const char *msg) {
808   char z[256];
809   const char *p;
810   static char hexa[17]="0123456789abcdef";
811   time_t now;
812   string s;
813 
814   if (env.Home.empty())
815     return;
816 
817   if (PauseLog)
818     msg=_("(message obfuscated -- password mode ?)");
819 
820   if (CommLog) {
821     snprintf(z,256,"%s/LOG.eboard",env.Home.c_str());
822 
823     ofstream f(z,ios::app);
824     if (!f) return;
825 
826     for(p=msg;*p;p++)
827       switch(*p) {
828       case '\n': s+="\\n"; break;
829       case '\r': s+="\\r"; break;
830       default:
831 	if (*p < 32) {
832 	  s+="(0x"; s+=hexa[(*p)>>4]; s+=hexa[(*p)&0xf]; s+=')';
833 	} else
834 	  s+=*p;
835       }
836 
837     now=time(0);
838     strftime(z,255,"%Y-%b-%d %H:%M:%S",localtime(&now));
839 
840     f << z << "[ " << ((int) getpid()) << "] " << s << endl;
841     f.close();
842   }
843 }
844 
dumpGames()845 void Global::dumpGames() {
846   cerr.setf(ios::dec,ios::basefield);
847   cerr << " GAME LIST (" << GameList.size() << " elements)\n";
848   cerr << "--------------------------------------------------------------------------\n";
849   for(GLi=GameList.begin();GLi!=GameList.end();GLi++)
850     (*GLi)->dump();
851   cerr << "--------------------------------------------------------------------------\n";
852 }
853 
dumpBoards()854 void Global::dumpBoards() {
855   cerr.setf(ios::dec,ios::basefield);
856   cerr << " BOARD LIST (" << BoardList.size() << " elements)\n";
857   cerr << "--------------------------------------------------------------------------\n";
858   for(BLi=BoardList.begin();BLi!=BoardList.end();BLi++)
859     (*BLi)->dump();
860   cerr << "--------------------------------------------------------------------------\n";
861 }
862 
dumpPanes()863 void Global::dumpPanes() {
864   cerr.setf(ios::dec,ios::basefield);
865   cerr << " PANE LIST\n";
866   cerr << "--------------------------------------------------------------------------\n";
867   ebook->dump();
868   cerr << "--------------------------------------------------------------------------\n";
869 }
870 
addHostBookmark(HostBookmark * hbm)871 void Global::addHostBookmark(HostBookmark *hbm) {
872   list<HostBookmark *>::iterator bi;
873 
874   for(bi=HostHistory.begin();bi!=HostHistory.end();bi++)
875     if ( (*(*bi)) == hbm ) {
876       delete hbm;
877       return;
878     }
879   HostHistory.push_front(hbm);
880   if (HostHistory.size() > 16) {
881     delete(HostHistory.back());
882     HostHistory.pop_back();
883   }
884 
885   writeRC();
886   if (bmlistener != 0) bmlistener->updateBookmarks();
887 }
888 
addEngineBookmark(EngineBookmark * ebm)889 void Global::addEngineBookmark(EngineBookmark *ebm) {
890   list<EngineBookmark *>::iterator ei;
891 
892   for(ei=EnginePresets.begin();ei!=EnginePresets.end();ei++)
893     if ( (*(*ei)) == ebm ) {
894       delete ebm;
895       return;
896     }
897 
898   EnginePresets.push_front(ebm);
899   if (EnginePresets.size() > 16) {
900     delete(EnginePresets.back());
901     EnginePresets.pop_back();
902   }
903 
904   writeRC();
905   if (bmlistener != 0) bmlistener->updateBookmarks();
906 }
907 
updateScrollBacks()908 void Global::updateScrollBacks() {
909   output->updateScrollBack();
910   updateChannelScrollBacks();
911 }
912 
getNotebook()913 Notebook * Global::getNotebook() {
914   return(ebook);
915 }
916 
filter(const char * s)917 const char * Global::filter(const char *s) {
918   int i,j;
919   string t;
920   gunichar uc;
921   const char *c;
922 
923   if (SpecialChars==0) return s; // no filtering
924 
925   j = strlen(s);
926   for(i=0;i<j;i++)
927     if (s[i] & 0x80 != 0)
928       break;
929   if (i==j) return(s); // ascii-clean, just return it
930 
931   for(c=s;*c!=0;c=g_utf8_next_char(c)) {
932     uc = g_utf8_get_char(c);
933     if (uc<128)
934       t.append( 1, (char) uc );
935     else {
936       switch(SpecialChars) {
937       case 1: break; // truncate
938       case 2: t.append(1,'_'); break; // underscores
939       case 3: unicodeNormalize(t,uc); break; // canonical decomposition
940       }
941     }
942   }
943 
944   return(strdup(t.c_str()));
945 }
946 
unicodeNormalize(string & dest,gunichar src)947 void Global::unicodeNormalize(string &dest, gunichar src) {
948   gunichar *tmp, tmpbuf[16];
949   gsize i,len;
950 
951 #if (GLIB_MAJOR_VERSION==2 && GLIB_MINOR_VERSION >= 30)
952   len = g_unichar_fully_decompose(src, FALSE, tmpbuf, 16);
953   tmp = &tmpbuf[0];
954 #else
955   tmp = g_unicode_canonical_decomposition(src, &len);
956 #endif
957 
958   for(i=0;i<len;i++) {
959     if (tmp[i] > 128) {
960       switch(tmp[i]) {
961       case 0x300:             tmp[i] = '`';  break; // grave
962       case 0xb4:  case 0x301: tmp[i] = '\''; break; // acute
963       case 0x302:             tmp[i] = '^';  break; // circumflex
964       case 0x303:             tmp[i] = '~';  break; // tilde
965       case 0xb8:  case 0x327: tmp[i] = ',';  break; // cedil
966       case 0x2d9: case 0x307: tmp[i] = '.';  break; // dot above
967       case 0x308:             tmp[i] = '\"'; break; // diaeresis
968       case 0x323:             tmp[i] = '.';  break; // dot below
969       default:
970 	//cout << "not found: " << ((int) tmp[i]) << endl;
971 	tmp[i] = '_';
972       }
973     }
974     dest.append( 1, (char) (tmp[i]&0x7f) );
975   }
976 
977 #if (!(GLIB_MAJOR_VERSION==2 && GLIB_MINOR_VERSION >= 30))
978   g_free(tmp);
979 #endif
980 }
981 
gatherConsoleState()982 void Global::gatherConsoleState() {
983   list<DetachedConsole *>::iterator i;
984 
985   // please make Desk.consoles empty before calling this. Thanks.
986 
987   for(i=Consoles.begin();i!=Consoles.end();i++)
988     Desk.addConsole(*i);
989 }
990 
991 // malloc has the stupid idea of segfaulting when
992 // allocating a word-incomplete size
safeMalloc(int nbytes)993 void * Global::safeMalloc(int nbytes) {
994   return(malloc(nbytes + (nbytes % 4)));
995 }
996 
setPieceSet(string & filename,bool chgPieces,bool chgSquares)997 void Global::setPieceSet(string &filename,bool chgPieces,bool chgSquares) {
998   string oldp,olds;
999   PieceSet *oldset=0;
1000 
1001   if (pieceset) {
1002     oldp=pieceset->getName();
1003     olds=pieceset->getSquareName();
1004     oldset=pieceset;
1005   } else {
1006     chgPieces=true;
1007     chgSquares=true;
1008   }
1009 
1010   pieceset=new PieceSet(chgPieces?filename:oldp,chgSquares?filename:olds);
1011   if (oldset)
1012     delete oldset;
1013 
1014   respawnPieceSet();
1015 }
1016 
respawnPieceSet()1017 void Global::respawnPieceSet() {
1018   list<PieceChangeListener *>::iterator i;
1019 
1020   // notify all objects that use the pieceset
1021   for(i=PieceClients.begin();i!=PieceClients.end();i++)
1022     (*i)->pieceSetChanged();
1023 }
1024 
addPieceClient(PieceChangeListener * pcl)1025 void Global::addPieceClient(PieceChangeListener *pcl) {
1026   global.debug("Global","addPieceClient");
1027   PieceClients.push_back(pcl);
1028 }
1029 
removePieceClient(PieceChangeListener * pcl)1030 void Global::removePieceClient(PieceChangeListener *pcl) {
1031   list<PieceChangeListener *>::iterator i;
1032 
1033   global.debug("Global","removePieceClient");
1034 
1035   for(i=PieceClients.begin();
1036       i!=PieceClients.end();
1037       i++)
1038     if ( (*i) == pcl ) {
1039       PieceClients.erase(i);
1040       return;
1041     }
1042 }
1043 
1044 // ----------
1045 
HostBookmark()1046 HostBookmark::HostBookmark() {
1047   memset(host,0,128);
1048   memset(protocol,0,64);
1049   port=0;
1050 }
1051 
operator ==(HostBookmark * hbm)1052 int HostBookmark::operator==(HostBookmark *hbm) {
1053   if (strcmp(host,hbm->host)) return 0;
1054   if (port!=hbm->port) return 0;
1055   if (strcmp(protocol,hbm->protocol)) return 0;
1056   return 1;
1057 }
1058 
operator ==(EngineBookmark * ebm)1059 int EngineBookmark::operator==(EngineBookmark *ebm) {
1060   if (humanwhite != ebm->humanwhite) return 0;
1061   if (timecontrol != ebm->timecontrol) return 0;
1062   if (maxply != ebm->maxply) return 0;
1063   if (think != ebm->think) return 0;
1064   if (proto != ebm->proto) return 0;
1065   if (mode != ebm->mode) return 0;
1066   if (directory.compare(ebm->directory)) return 0;
1067   if (cmdline.compare(ebm->cmdline))     return 0;
1068   return 1;
1069 }
1070 
read(tstring & t)1071 void EngineBookmark::read(tstring &t) {
1072   static const char *sep="^\n\r";
1073   string *p;
1074 
1075   caption     = *(t.token(sep));
1076   directory   = *(t.token(sep));
1077   cmdline     = *(t.token(sep));
1078   humanwhite  = t.tokenvalue(sep);
1079 
1080   p = t.token(sep);
1081   if (p)
1082     timecontrol.fromSerialization(p->c_str());
1083 
1084   maxply      = t.tokenvalue(sep);
1085   think       = t.tokenvalue(sep);
1086   proto       = t.tokenvalue(sep);
1087   mode        = (variant) t.tokenvalue(sep);
1088 
1089   if (!directory.compare("NULL")) directory.erase();
1090   if (!cmdline.compare("NULL")) cmdline.erase();
1091 }
1092 
1093 // -------------------------------- channel splitting
1094 
IcsChannel(char * s)1095 IcsChannel::IcsChannel(char *s) {
1096   static const char *sep="\t\r\n";
1097   tstring t;
1098   t.set(s);
1099   number = t.tokenvalue(sep);
1100   name   = * (t.token(sep));
1101 }
1102 
getChannels(char * ipaddr)1103 void ChannelSplitter::getChannels(char *ipaddr) {
1104   char destname[512], url[512];
1105   struct stat age;
1106   time_t now, d;
1107   pid_t kid;
1108 
1109   global.debug("ChannelSplitter","getChannels",ipaddr);
1110   channels.clear();
1111 
1112   if (! global.RetrieveChannelNames)
1113     return;
1114 
1115   snprintf(destname,512,"/tmp/eboard-chlist-%s-%d.tmp", ipaddr, getuid() );
1116   chlist=destname;
1117 
1118   if (stat(destname, &age)==0) {
1119     now=time(0);
1120     d = now - age.st_mtime;
1121     // list expires after 8 hours
1122     if (d < 28800)
1123       goto cs_gc_use_current;
1124   }
1125 
1126   snprintf(url,512,"http://www.bergo.eng.br/eboard/ics/%s.txt",ipaddr);
1127 
1128   kid=fork();
1129   if (kid==0) {
1130 
1131     execlp("wget","wget","-q","-O",destname,url,0);
1132     _exit(0);
1133 
1134   } else {
1135 
1136     global.zombies.add(kid, this);
1137 
1138   }
1139 
1140  cs_gc_use_current:
1141   parseChannelList();
1142 
1143 }
1144 
ZombieNotification(int pid)1145 void ChannelSplitter::ZombieNotification(int pid) {
1146   parseChannelList();
1147 }
1148 
parseChannelList()1149 void ChannelSplitter::parseChannelList() {
1150   char s[512];
1151 
1152   global.debug("ChannelSplitter","parseChannelList");
1153   channels.clear();
1154 
1155   ifstream f(chlist.c_str());
1156   if (!f) {
1157     global.debug("ChannelSplitter","parseChannelList","can't read file");
1158     return;
1159   }
1160 
1161   if (memset(s,0,512), f.getline(s,511,'\n')) {
1162 
1163     if (strstr(s,"text/ics-channel-list")) {
1164       while( memset(s,0,512), f.getline(s,511,'\n') ) {
1165 	if (!isdigit(s[0])) break;
1166 	channels.push_back( IcsChannel(s) );
1167       }
1168     }
1169 
1170   }
1171 
1172   f.close();
1173 }
1174 
getChannelTitle(int n)1175 const char * ChannelSplitter::getChannelTitle(int n) {
1176   int i,j;
1177   static char z[128];
1178   j=channels.size();
1179   for(i=0;i<j;i++) {
1180     if (n==channels[i].number) {
1181       snprintf(z,128,"#%d: %s",n,channels[i].name.c_str());
1182       return z;
1183     }
1184   }
1185   snprintf(z,128,"#%d",n);
1186   return z;
1187 }
1188 
ensurePane(int ch)1189 void ChannelSplitter::ensurePane(int ch) {
1190   int i,j;
1191   j=panes.size();
1192   for(i=0;i<j;i++)
1193     if (numbers[i]==ch)
1194       return; // already exists
1195   createPane(ch);
1196 }
1197 
createPane(int ch)1198 void ChannelSplitter::createPane(int ch) {
1199   Notebook *nb;
1200   Text *op;
1201   char z[64];
1202   nb=getNotebook();
1203   if (!nb) return;
1204   op=new Text();
1205 
1206   snprintf(z,64,"%s",getChannelTitle(ch) );
1207 
1208   op->show();
1209   nb->addPage(op->widget,z,-200-ch,true);
1210   op->setNotebook(nb,-200-ch);
1211   numbers.push_back(ch);
1212   panes.push_back(op);
1213 }
1214 
channelPageUp(int ch)1215 void ChannelSplitter::channelPageUp(int ch) {
1216   int i,j;
1217   j=panes.size();
1218   for(i=0;i<j;i++)
1219     if (numbers[i] == ch) {
1220       panes[i]->pageUp();
1221       return;
1222     }
1223 }
1224 
channelPageDown(int ch)1225 void ChannelSplitter::channelPageDown(int ch) {
1226   int i,j;
1227   j=panes.size();
1228   for(i=0;i<j;i++)
1229     if (numbers[i] == ch) {
1230       panes[i]->pageDown();
1231       return;
1232     }
1233 }
1234 
appendToChannel(int ch,char * msg,int color,Importance im)1235 void ChannelSplitter::appendToChannel(int ch,char *msg,int color,Importance im) {
1236   int i,j;
1237   ensurePane(ch);
1238   j=panes.size();
1239   for(i=0;i<j;i++)
1240     if (numbers[i]==ch) {
1241       panes[i]->append(msg,color,im);
1242       panes[i]->contentUpdated();
1243       return;
1244     }
1245 }
1246 
removeRemovablePage(int n)1247 void ChannelSplitter::removeRemovablePage(int n) {
1248   int rn;
1249   int i,j;
1250   Notebook *nb;
1251 
1252   rn= -n;
1253   rn-=200;
1254   nb=getNotebook();
1255 
1256   j=panes.size();
1257   for(i=0;i<j;i++)
1258     if (numbers[i]==rn) {
1259       nb->removePage(n);
1260       delete panes[i];
1261       panes.erase(panes.begin() + i);
1262       numbers.erase(numbers.begin() + i);
1263     }
1264 }
1265 
updateChannelScrollBacks()1266 void ChannelSplitter::updateChannelScrollBacks() {
1267   int i,j;
1268   j=panes.size();
1269   for(i=0;i<j;i++)
1270     panes[i]->updateScrollBack();
1271 }
1272 
updateFont()1273 void ChannelSplitter::updateFont() {
1274   int i,j;
1275   j=panes.size();
1276   for(i=0;i<j;i++)
1277     panes[i]->updateFont();
1278 }
1279 
TerminalColor()1280 TerminalColor::TerminalColor() {
1281   TextDefault   = 0xeeeeee;
1282   TextBright    = 0xffffff;
1283   PrivateTell   = 0xffff00;
1284   NewsNotify    = 0xff8080;
1285   Mamer         = 0xffdd00;
1286   KibitzWhisper = 0xd38fd3;
1287   Shouts        = 0xddffdd;
1288   Seeks         = 0x80ff80;
1289   ChannelTell   = 0x3cd9d1;
1290   Engine        = 0xc0ff60;
1291   Background    = 0;
1292 }
1293 
read(tstring & t)1294 void TerminalColor::read(tstring &t) {
1295   static const char *comma=",:\n\r \t";
1296 
1297   TextDefault   = t.tokenvalue(comma,16);
1298   TextBright    = t.tokenvalue(comma,16);
1299   PrivateTell   = t.tokenvalue(comma,16);
1300   NewsNotify    = t.tokenvalue(comma,16);
1301   Mamer         = t.tokenvalue(comma,16);
1302   KibitzWhisper = t.tokenvalue(comma,16);
1303   Shouts        = t.tokenvalue(comma,16);
1304   Seeks         = t.tokenvalue(comma,16);
1305   ChannelTell   = t.tokenvalue(comma,16);
1306   Engine        = t.tokenvalue(comma,16);
1307   Background    = t.tokenvalue(comma,16);
1308 }
1309 
1310 // ---- desktop saving
1311 
WindowGeometry(int a,int b,int c,int d)1312 WindowGeometry::WindowGeometry(int a,int b,int c,int d) {
1313   X=a; Y=b; W=c; H=d;
1314 }
1315 
WindowGeometry()1316 WindowGeometry::WindowGeometry() {
1317   setNull();
1318 }
1319 
print()1320 void WindowGeometry::print() {
1321   cout << "X,Y,W,H = " << X << "," << Y << "," << W << "," << H << endl;
1322 }
1323 
retrieve(GtkWidget * w)1324 void WindowGeometry::retrieve(GtkWidget *w) {
1325   gint a[7];
1326   gdk_window_get_geometry(w->window,a,a+1,a+2,a+3,a+4);
1327   gdk_window_get_origin(w->window,a+5,a+6);
1328   X=a[5]-a[0];
1329   Y=a[6]-a[1];
1330   W=a[2];
1331   H=a[3];
1332 }
1333 
isNull()1334 bool WindowGeometry::isNull() {
1335   return( (X==0)&&(Y==0)&&(W==0)&&(H==0) );
1336 }
1337 
setNull()1338 void WindowGeometry::setNull() {
1339   X=Y=W=H=0;
1340 }
1341 
read(tstring & t)1342 void WindowGeometry::read(tstring &t) {
1343   static const char *sep=":,()\n\t\r ";
1344   X=t.tokenvalue(sep);
1345   Y=t.tokenvalue(sep);
1346   W=t.tokenvalue(sep);
1347   H=t.tokenvalue(sep);
1348 }
1349 
1350 // --------
1351 
Desktop()1352 Desktop::Desktop() {
1353   clear();
1354 }
1355 
clear()1356 void Desktop::clear() {
1357   vector<WindowGeometry *>::iterator i;
1358   vector<string *>::iterator j;
1359 
1360   wMain.setNull();
1361   wGames.setNull();
1362   wLocal.setNull();
1363   wAds.setNull();
1364 
1365   for(i=consoles.begin();i!=consoles.end();i++)
1366     delete(*i);
1367 
1368   for(j=cfilters.begin();j!=cfilters.end();j++)
1369     delete(*j);
1370 
1371   consoles.clear();
1372   cfilters.clear();
1373   PanePosition = 0;
1374 }
1375 
read(tstring & t)1376 void Desktop::read(tstring &t) {
1377   static const char *sep=":,()\n\t\r ";
1378   wMain.read(t);
1379   wGames.read(t);
1380   wLocal.read(t);
1381   wAds.read(t);
1382   global.Desk.PanePosition = t.tokenvalue(sep);
1383 }
1384 
writeConsoles(ostream & s,const char * key)1385 void Desktop::writeConsoles(ostream &s, const char *key) {
1386   int i,j;
1387   j=consoles.size();
1388   for(i=0;i<j;i++) {
1389     s << key << "::" << (*consoles[i]);
1390     s << (*(cfilters[i])) << endl;
1391   }
1392 }
1393 
readConsole(tstring & t)1394 void Desktop::readConsole(tstring &t) {
1395   WindowGeometry *wg;
1396   string *p,*s;
1397   static const char *sep="\n\r";
1398 
1399   wg=new WindowGeometry();
1400   wg->read(t);
1401 
1402   p=t.token(sep);
1403   s=new string();
1404   if (p) (*s)=(*p);
1405 
1406   consoles.push_back(wg);
1407   cfilters.push_back(s);
1408 }
1409 
addConsole(DetachedConsole * dc)1410 void Desktop::addConsole(DetachedConsole *dc) {
1411   WindowGeometry *wg;
1412   wg=new WindowGeometry();
1413   wg->retrieve(dc->widget);
1414   consoles.push_back(wg);
1415   cfilters.push_back(new string(dc->getFilter()));
1416 }
1417 
spawnConsoles(TextSet * ts)1418 void Desktop::spawnConsoles(TextSet *ts) {
1419   int i,j;
1420   char tmp[512];
1421   DetachedConsole *dc;
1422   j=consoles.size();
1423 
1424   for(i=0;i<j;i++) {
1425     dc=new DetachedConsole(ts,0);
1426     dc->show();
1427     dc->restorePosition(consoles[i]);
1428     if (cfilters[i]->size()) {
1429       g_strlcpy(tmp,cfilters[i]->c_str(),512);
1430       dc->setFilter(tmp);
1431     }
1432   }
1433 }
1434 
1435 // ------- ah, the zombies
1436 
ZombieHunter()1437 ZombieHunter::ZombieHunter() {
1438   signal(SIGCHLD,zh_sigchild_handler);
1439 }
1440 
~ZombieHunter()1441 ZombieHunter::~ZombieHunter() {
1442   pids.clear();
1443   handlers.clear();
1444 }
1445 
add(int pid,SigChildHandler * sigh)1446 void ZombieHunter::add(int pid, SigChildHandler *sigh) {
1447   pids.push_back(pid);
1448   handlers.push_back(sigh);
1449 }
1450 
handleSigChild()1451 void ZombieHunter::handleSigChild() {
1452   pid_t epid;
1453   unsigned int i;
1454   int s;
1455 
1456   while ( ( epid = waitpid(-1,&s,WNOHANG) ) > 0 ) {
1457     for(i=0;i<pids.size();i++)
1458       if (pids[i] == epid) {
1459 	if (handlers[i] != 0) handlers[i]->ZombieNotification(epid);
1460 	pids.erase(pids.begin() + i);
1461 	handlers.erase(handlers.begin() + i);
1462 	break;
1463       }
1464   }
1465 }
1466 
zh_sigchild_handler(int sig)1467 void zh_sigchild_handler(int sig) {
1468   if (sig == SIGCHLD)
1469     global.zombies.handleSigChild();
1470 }
1471 
Environment()1472 Environment::Environment() {
1473   char *p;
1474 
1475   p=getenv("HOME");
1476   if (p) {
1477     Home=p;
1478   } else {
1479     Home.erase();
1480     cerr << _("** eboard ** warning: HOME environment variable not set\n");
1481   }
1482 
1483   p=getenv("USER");
1484   if (p) {
1485     User=p;
1486   } else {
1487     User=_("Human");
1488   }
1489 
1490   if (!Home.empty()) {
1491     Config=Home;
1492     Config+="/.eboard/eboard.conf";
1493   } else {
1494     Config.erase();
1495   }
1496 }
1497 
1498 
1499 
1500