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 "eboard.h"
30 #include "protocol.h"
31 #include "position.h"
32 #include "chess.h"
33 #include "global.h"
34 #include "tstring.h"
35 
P2PProtocol()36 P2PProtocol::P2PProtocol() {
37   tmpbuf = (char *) malloc(2048);
38 
39   Chat.set("C-*");
40   ProtoGreet.set("P-(*)-(*)-(*)*"); /* (EBOARDP2P)-(software)-(protover) */
41   NameGreet.set("N-(*)*");
42   GameProp.set("G-(*)-(*)-(*)-(*)*"); /* amwhite / time / inc / variant */
43   Accept.set("A-G");
44   Move.set("M-(*)");
45   Outcome.set("R-(*)-(*)*");
46   DrawOffer.set("D-O");
47 
48   Binder.add(&Chat, &ProtoGreet, &NameGreet,
49 	     &GameProp, &Accept, &Move, &Outcome,
50 	     &DrawOffer, NULL);
51 
52   IdSent = IdReceived = false;
53 
54   strcpy(RemotePlayer,"Remote");
55   strcpy(RemoteSoftware, "Software");
56   RemoteProtover = 1;
57 
58   pad = 0;
59   GotProp = false;
60   GotDrawProp = SentDrawProp = false;
61   MyGame = 0;
62 }
63 
~P2PProtocol()64 P2PProtocol::~P2PProtocol() {
65   if (tmpbuf)
66     free(tmpbuf);
67   tmpbuf = 0;
68 }
69 
requiresLegalityChecking()70 bool P2PProtocol::requiresLegalityChecking() {
71   return true;
72 }
73 
finalize()74 void P2PProtocol::finalize() {
75   if (pad) {
76     pad->release();
77     pad = 0;
78   }
79 }
80 
hail()81 void P2PProtocol::hail() {
82   sendId();
83 }
84 
sendId()85 void P2PProtocol::sendId() {
86   char z[128];
87   if (!IdSent)
88     if (global.network)
89       if (global.network->isConnected()) {
90 	snprintf(z,128,"P-(EBOARDP2P)-(eboard %s)-(1)",global.Version);
91 	global.network->writeLine(z);
92 	snprintf(z,128,"N-(%s)",global.P2PName);
93 	global.network->writeLine(z);
94 	IdSent = true;
95       }
96 }
97 
sendProposal(GameProposal & g)98 void P2PProtocol::sendProposal(GameProposal &g) {
99   char z[128];
100   if (global.network)
101     if (global.network->isConnected()) {
102       snprintf(z,128,"G-(%d)-(%d)-(%d)-(%s)",
103 	       g.AmWhite ? 0 : 1,
104 	       g.timecontrol.value[0], g.timecontrol.value[1], ChessGame::variantName(g.Variant));
105       global.network->writeLine(z);
106       MyProp = g;
107       global.status->setText(_("Game proposal sent."),10);
108     }
109 }
110 
acceptProposal()111 void P2PProtocol::acceptProposal() {
112   char z[64];
113 
114   if (GotDrawProp) {
115     draw();
116     return;
117   }
118 
119   if (GotProp) {
120 
121     if (MyGame) {
122       global.status->setText(_("Finish the current game first."),10);
123       return;
124     }
125     strcpy(z,"A-G");
126     if (global.network)
127       if (global.network->isConnected())
128 	global.network->writeLine(z);
129     GotProp = false;
130     createGame(HisProp);
131   }
132 }
133 
receiveString(char * netstring)134 void P2PProtocol::receiveString(char *netstring) {
135   int j;
136   char *p, z[256], y[128];
137 
138   j = strlen(netstring);
139   if (j<2) return;
140   if (netstring[1] != '-') return;
141 
142   Binder.prepare(netstring);
143 
144   if (Chat.match()) {
145     global.output->append(Chat.getStarToken(0),
146 			  global.Colors.PrivateTell,
147 			  IM_PERSONAL);
148     return;
149   }
150 
151   if (MyGame && Move.match()) {
152     Position cur;
153     int wc, bc;
154 
155     cur  = MyGame->getLastPosition();
156     cur.SANstring(Move.getStarToken(0), y);
157     cur.moveAnyNotation(Move.getStarToken(0),
158 			Current.AmWhite ? BLACK : WHITE,
159 			Current.Variant);
160     if (Current.AmWhite)
161       snprintf(z,256,"%d. ... %s",MoveNumber,y);
162     else
163       snprintf(z,256,"%d. %s",MoveNumber,y);
164 
165     if (MoveNumber == 1)
166       wc = bc = 1000 * Current.timecontrol.value[0];
167     else {
168       MyGame->incrementActiveClock(Current.timecontrol.value[1]);
169       wc = bc = CLOCK_UNCHANGED;
170     }
171 
172     MyGame->updatePosition2(cur, MoveNumber,
173 			    cur.sidehint?0:1,
174 			    wc, bc, z, true);
175 
176     if (Current.AmWhite) ++MoveNumber;
177     global.BoardList.front()->setCanMove(true);
178     global.opponentMoved();
179 
180     if (GotDrawProp) pad->resetDrawProp();
181     GotDrawProp = SentDrawProp = false;
182     return;
183   }
184 
185   if (ProtoGreet.match()) {
186     p = ProtoGreet.getStarToken(0);
187     if (strcmp(p, "EBOARDP2P")!=0) {
188       global.status->setText(_("Protocol mismatch, disconnecting."),10);
189       global.network->close();
190       return;
191     }
192     p = ProtoGreet.getStarToken(1);
193     memset(RemoteSoftware,0,128);
194     g_strlcpy(RemoteSoftware,p,128);
195 
196     RemoteProtover = atoi(ProtoGreet.getStarToken(2));
197     if (RemoteProtover < 1) {
198       global.status->setText(_("Protocol mismatch, disconnecting."),10);
199       global.network->close();
200       return;
201     }
202 
203     snprintf(z,256,"Remote software: %s (eboard/p2p v.%d)",
204 	     RemoteSoftware,RemoteProtover);
205     global.output->append(z,global.Colors.TextBright,IM_NORMAL);
206     IdReceived = true;
207     sendId();
208 
209     if (!pad) {
210       pad = new P2PPad(this);
211       pad->show();
212     }
213 
214     return;
215   }
216 
217   if (NameGreet.match()) {
218     memset(RemotePlayer,0,64);
219     g_strlcpy(RemotePlayer,NameGreet.getStarToken(0),64);
220     snprintf(z,256,"Remote player name: %s",
221 	     RemotePlayer);
222     global.output->append(z,global.Colors.TextBright,IM_NORMAL);
223     sendId();
224     return;
225   }
226 
227   if (GameProp.match()) {
228     GameProposal g;
229     int a,b;
230 
231     g.AmWhite = (atoi(GameProp.getStarToken(0)) != 0);
232     a = atoi(GameProp.getStarToken(1));
233     b = atoi(GameProp.getStarToken(2));
234     g.timecontrol.setIcs(a,b);
235     g.Variant = ChessGame::variantFromName(GameProp.getStarToken(3));
236 
237     HisProp = g;
238     GotProp = true;
239     if (pad)
240       pad->setProposal(g);
241     snprintf(z,256,_("Received a game proposal from %s."),RemotePlayer);
242     global.output->append(z,global.Colors.TextBright,IM_NORMAL);
243     return;
244   }
245 
246   if (Accept.match()) {
247     snprintf(z,256,_("%s accepted your game proposal."),RemotePlayer);
248     global.output->append(z,global.Colors.TextBright,IM_NORMAL);
249     createGame(MyProp);
250     return;
251   }
252 
253   if (DrawOffer.match()) {
254     if (SentDrawProp) {
255       draw();
256       return;
257     } else {
258       char z[128];
259       pad->setDrawProp();
260       snprintf(z,128,_("%s offers a draw."),RemotePlayer);
261       global.output->append(z,global.Colors.TextBright,IM_PERSONAL);
262       GotDrawProp = true;
263       GotProp = false;
264       return;
265     }
266   }
267 
268   if (Outcome.match()) {
269     p=Outcome.getStarToken(0);
270     y[0] = *p;
271     p=Outcome.getStarToken(1);
272     y[1] = *p;
273 
274     switch(y[1]) {
275     case 'S': // stalemate
276       gameOver(DRAW, _("Stalemate"));
277       return;
278     case 'N': // no material
279       gameOver(DRAW, _("No material to mate"));
280       return;
281     case 'M': // checkmate
282       gameOver(y[0]=='W'?WHITE_WIN:BLACK_WIN,_("Checkmate"));
283       return;
284     case 'R': // resign
285       gameOver(y[0]=='W'?WHITE_WIN:BLACK_WIN,_("Player resigns"));
286       return;
287     case 'A': //abort
288       gameOver(UNDEF,_("Game Aborted"));
289       return;
290     case 'D': // draw by agreement
291       gameOver(DRAW, _("Drawn by agreement"));
292       return;
293     default:
294       gameOver(UNDEF, _("Unknown result"));
295       return;
296     }
297   }
298 
299   if (strlen(netstring) < 200)
300     snprintf(z,256,"Got garbage: [%s]",netstring);
301   else
302     snprintf(z,256,"Got too much garbage.");
303   global.output->append(z,global.Colors.Engine,IM_NORMAL);
304 }
305 
sendUserInput(char * line)306 void P2PProtocol::sendUserInput(char *line) {
307   int j;
308 
309   memset(tmpbuf,0,2048);
310   snprintf(tmpbuf,2048,"C-%s> ",global.P2PName);
311   j = strlen(line);
312   strncat(tmpbuf, line, 1900);
313   if (global.network)
314     if (global.network->isConnected())
315       global.network->writeLine(tmpbuf);
316 }
317 
createGame(GameProposal & g)318 void P2PProtocol::createGame(GameProposal &g) {
319   ChessGame *cg;
320   Position *p;
321 
322   Current = g;
323 
324   cg = new ChessGame();
325   cg->source = GS_Other;
326   cg->clock_regressive = 1;
327   cg->GameNumber=global.nextFreeGameId(P2P_GAME+1);
328   g_strlcpy(cg->PlayerName[g.AmWhite?0:1],global.P2PName,64);
329   g_strlcpy(cg->PlayerName[g.AmWhite?1:0],RemotePlayer,64);
330   cg->MyColor = g.AmWhite ? WHITE : BLACK;
331   cg->Rated = 0;
332   cg->Variant = g.Variant;
333   cg->timecontrol = g.timecontrol;
334   cg->AmPlaying = true;
335 
336   global.prependGame(cg);
337   global.BoardList.front()->reset();
338 
339   cg->setBoard(global.BoardList.front());
340   global.BoardList.front()->setGame(cg);
341   global.BoardList.front()->pop();
342   global.BoardList.front()->setCanMove(g.AmWhite);
343   global.BoardList.front()->repaint();
344   global.BoardList.front()->setFlipped(!g.AmWhite);
345 
346   cg->acknowledgeInfo();
347   cg->fireWhiteClock(g.timecontrol.value[0], g.timecontrol.value[1]);
348 
349   p = new Position();
350   cg->updatePosition2(*p,1,p->sidehint ? 0:1,
351 		      1000*g.timecontrol.value[0],
352 		      1000*g.timecontrol.value[0],
353 		      "0. startpos");
354   delete p;
355 
356   global.status->setText(_("Game started!"),10);
357   global.gameStarted();
358 
359   MyGame = cg;
360   MoveNumber = 1;
361 }
362 
sendMove(int x1,int y1,int x2,int y2,int prom)363 void P2PProtocol::sendMove(int x1,int y1,int x2,int y2,int prom) {
364   char move[7], xm[12], san[16], xsan[20];
365   piece pp;
366   Position cur;
367   int wc,bc;
368 
369   global.debug("P2PProtocol","sendMove");
370 
371   if (!MyGame)
372     return;
373 
374   pp = prom ? global.promotion->getPiece() : EMPTY;
375 
376   cur = MyGame->getLastPosition();
377 
378   if (!cur.isMoveLegalCartesian(x1,y1,x2,y2,
379 				Current.AmWhite?WHITE:BLACK,
380 				Current.Variant)) {
381     global.status->setText(_("Illegal move, not sent."), 10);
382     return;
383   }
384 
385   cur.stdNotationForMove(x1,y1,x2,y2,pp,san,MyGame->Variant);
386   cur.moveCartesian(x1,y1,x2,y2,MyGame->Variant,true);
387 
388   if (Current.AmWhite)
389     snprintf(xsan,20,"%d. %s",MoveNumber,san);
390   else
391     snprintf(xsan,20,"%d. ... %s",MoveNumber,san);
392 
393   if (MoveNumber == 1)
394     wc = bc = 1000 * Current.timecontrol.value[0];
395   else {
396     wc = bc = CLOCK_UNCHANGED;
397     MyGame->incrementActiveClock(Current.timecontrol.value[1]);
398   }
399 
400   if (!Current.AmWhite) ++MoveNumber;
401 
402   MyGame->updatePosition2(cur,MoveNumber,
403 			  cur.sidehint ? 0:1, wc,bc, xsan);
404 
405   /* -- */
406 
407   move[4]=0;
408   move[5]=0;
409   move[6]=0;
410   move[0]='a'+x1;
411   move[1]='1'+y1;
412   move[2]='a'+x2;
413   move[3]='1'+y2;
414 
415   if (prom) {
416     pp=global.promotion->getPiece();
417     move[4]='=';
418     switch(pp) {
419     case QUEEN:   move[5]='Q'; break;
420     case ROOK:    move[5]='R'; break;
421     case BISHOP:  move[5]='B'; break;
422     case KNIGHT:  move[5]='N'; break;
423     case KING:    move[5]='K'; break;
424     }
425   }
426   snprintf(xm,12,"M-(%s)",move);
427   global.network->writeLine(xm);
428   global.BoardList.front()->setCanMove(false);
429 
430   /* check if move ends the game */
431 
432   if (cur.isStalemate(Current.AmWhite?BLACK:WHITE,Current.Variant)) {
433     snprintf(xm,12,"R-(D)-(S)");
434     global.network->writeLine(xm);
435     gameOver(DRAW, _("Stalemate"));
436     return;
437   }
438   if (cur.isNMDraw(Current.Variant)) {
439     snprintf(xm,12,"R-(D)-(N)");
440     global.network->writeLine(xm);
441     gameOver(DRAW, _("No material to mate"));
442     return;
443   }
444   if (cur.isMate(Current.AmWhite?BLACK:WHITE,Current.Variant)) {
445     snprintf(xm,12,"R-(%c)-(M)", Current.AmWhite?'W':'B');
446     global.network->writeLine(xm);
447     gameOver(Current.AmWhite?WHITE_WIN:BLACK_WIN, _("Checkmate"));
448     return;
449   }
450 
451   if (GotDrawProp) pad->resetDrawProp();
452   GotDrawProp = SentDrawProp = false;
453 }
454 
gameOver(GameResult gr,char * desc)455 void P2PProtocol::gameOver(GameResult gr, char *desc) {
456   char z[256];
457   if (!MyGame) return;
458   MyGame->endGame(desc, gr);
459 
460   snprintf(z,256,_("Game over: %s"), desc);
461   global.output->append(z, global.Colors.TextBright,
462 			IM_NORMAL);
463 
464   if (global.AppendPlayed) {
465     if  (MyGame->savePGN(global.AppendFile,true)) {
466       snprintf(z,256,_("Game appended to %s"),global.AppendFile);
467       global.status->setText(z,10);
468     }
469   }
470 
471   if ((gr==WHITE_WIN && Current.AmWhite)||
472       (gr==BLACK_WIN && !Current.AmWhite))
473     global.gameWon();
474 
475   if ((gr==WHITE_WIN && !Current.AmWhite)||
476       (gr==BLACK_WIN && Current.AmWhite))
477     global.gameLost();
478 
479   MyGame = 0;
480   GotDrawProp = SentDrawProp = false;
481 }
482 
resign()483 void P2PProtocol::resign() {
484   char z[64];
485   if (MyGame) {
486     snprintf(z,64,"R-(%c)-(R)",Current.AmWhite?'B':'W');
487     global.network->writeLine(z);
488     gameOver(Current.AmWhite?BLACK_WIN:WHITE_WIN,_("Player resigns"));
489   }
490 }
491 
draw()492 void P2PProtocol::draw() {
493   char z[128];
494   if (GotDrawProp) {
495     gameOver(DRAW,_("Drawn by agreement"));
496     global.network->writeLine("R-(D)-(D)");
497   } else {
498     global.network->writeLine("D-O");
499     SentDrawProp = true;
500     g_strlcpy(z,_("Draw offer sent."),128);
501     global.status->setText(z,5);
502   }
503 }
504 
adjourn()505 void P2PProtocol::adjourn() {
506   global.status->setText("Adjournment is not supported.",10);
507 }
508 
abort()509 void P2PProtocol::abort() {
510   char z[64];
511   if (MyGame) {
512     snprintf(z,64,"R-(D)-(A)");
513     global.network->writeLine(z);
514     gameOver(UNDEF,_("Game Aborted"));
515   }
516 }
517 
getRemotePlayer()518 char * P2PProtocol::getRemotePlayer() { return(RemotePlayer); }
519 
520 /* =================================== */
521 
P2PPad(P2PProtocol * _proto)522 P2PPad::P2PPad(P2PProtocol *_proto) : NonModalDialog("Direct Connection") {
523   GtkWidget *v, *t, *f, *h, *match, *f2, *v2, *h2;
524   int i;
525 
526   proto = _proto;
527   setCloseable(false);
528   PropIsDraw = false;
529 
530   v = gtk_vbox_new(FALSE, 2);
531   gtk_container_add(GTK_CONTAINER(widget), v);
532 
533   t = gtk_table_new(4,4,FALSE);
534   gtk_table_set_row_spacings(GTK_TABLE(t), 4);
535   gtk_table_set_col_spacings(GTK_TABLE(t), 4);
536   gtk_container_set_border_width(GTK_CONTAINER(t), 4);
537 
538   f = gtk_frame_new(_("Propose Game"));
539   gtk_frame_set_shadow_type(GTK_FRAME(f), GTK_SHADOW_ETCHED_IN);
540   gtk_container_set_border_width(GTK_CONTAINER(f), 4);
541 
542   gtk_box_pack_start(GTK_BOX(v), f, TRUE, TRUE, 2);
543   gtk_container_add(GTK_CONTAINER(f), t);
544 
545   bl[0] = new BoxedLabel(_("Your color:"));
546   bl[1] = new BoxedLabel(_("Initial time ([mm:]ss):"));
547   bl[2] = new BoxedLabel(_("Increment (secs):"));
548 
549   color   = new DropBox(_("White"),
550 			_("Black"),NULL);
551 
552   wtime   = gtk_entry_new_with_max_length(8);
553   winc    = gtk_entry_new_with_max_length(4);
554 
555   gtk_entry_set_text(GTK_ENTRY(wtime),"45:00");
556   gtk_entry_set_text(GTK_ENTRY(winc),"0");
557 
558   gtk_table_attach_defaults(GTK_TABLE(t), bl[0]->widget, 0,1, 0,1);
559   gtk_table_attach_defaults(GTK_TABLE(t), color->widget, 1,2, 0,1);
560   gtk_table_attach_defaults(GTK_TABLE(t), bl[1]->widget, 0,1, 1,2);
561   gtk_table_attach_defaults(GTK_TABLE(t), wtime, 1,2, 1,2);
562   gtk_table_attach_defaults(GTK_TABLE(t), bl[2]->widget, 0,1, 2,3);
563   gtk_table_attach_defaults(GTK_TABLE(t), winc, 1,2, 2,3);
564 
565   h=gtk_hbutton_box_new();
566   gtk_button_box_set_layout(GTK_BUTTON_BOX(h), GTK_BUTTONBOX_END);
567   gtk_button_box_set_spacing(GTK_BUTTON_BOX(h), 5);
568 
569   gtk_table_attach_defaults(GTK_TABLE(t), h, 0,2, 3,4);
570 
571   match = gtk_button_new_with_label(_("Propose"));
572   gtk_box_pack_start(GTK_BOX(h), match, TRUE, TRUE, 0);
573 
574   gtk_signal_connect(GTK_OBJECT(match),"clicked",
575 		     GTK_SIGNAL_FUNC(p2ppad_propose), (gpointer) this);
576 
577   color->show();
578   for(i=0;i<3;i++) bl[i]->show();
579 
580   f2 = gtk_frame_new(_("Last Proposal Received"));
581   gtk_frame_set_shadow_type(GTK_FRAME(f2), GTK_SHADOW_ETCHED_IN);
582   gtk_container_set_border_width(GTK_CONTAINER(f2), 4);
583   gtk_box_pack_start(GTK_BOX(v), f2, TRUE, TRUE, 2);
584 
585   v2 = gtk_vbox_new(FALSE,4);
586   gtk_container_add(GTK_CONTAINER(f2), v2);
587 
588   wprop = gtk_label_new(_("No proposals received."));
589   gtk_label_set_justify(GTK_LABEL(wprop), GTK_JUSTIFY_LEFT);
590   gtk_box_pack_start(GTK_BOX(v2), wprop, TRUE, TRUE, 2);
591 
592   h2=gtk_hbutton_box_new();
593   gtk_button_box_set_layout(GTK_BUTTON_BOX(h2), GTK_BUTTONBOX_END);
594   gtk_button_box_set_spacing(GTK_BUTTON_BOX(h2), 5);
595   gtk_box_pack_start(GTK_BOX(v2), h2, FALSE, TRUE, 2);
596 
597   wacc = gtk_button_new_with_label(_("Accept"));
598   gtk_box_pack_start(GTK_BOX(h2), wacc, TRUE, TRUE, 0);
599 
600   gtk_widget_set_sensitive(wacc, FALSE);
601 
602   gtk_signal_connect(GTK_OBJECT(wacc),"clicked",
603 		     GTK_SIGNAL_FUNC(p2ppad_accept), (gpointer) this);
604 
605   Gtk::show(wtime,winc,f,t,h,match, wacc, h2, wprop, v2, f2, v, NULL);
606 }
607 
~P2PPad()608 P2PPad::~P2PPad(){
609   int i;
610   for(i=0;i<3;i++)
611     delete(bl[i]);
612   delete color;
613 }
614 
setProposal(GameProposal & g)615 void P2PPad::setProposal(GameProposal &g) {
616   char z[512];
617 
618   snprintf(z,512,_("%s (white) vs. %s (black)\n%s\n%d:%.2d %d"),
619 	   g.AmWhite   ? global.P2PName : proto->getRemotePlayer(),
620 	   (!g.AmWhite) ? global.P2PName : proto->getRemotePlayer(),
621 	   ChessGame::variantName(g.Variant),
622 	   g.timecontrol.value[0] / 60,
623 	   g.timecontrol.value[0] % 60,
624 	   g.timecontrol.value[1]);
625   gtk_widget_set_sensitive(wacc, TRUE);
626   gtk_label_set_text(GTK_LABEL(wprop), z);
627   gtk_widget_queue_resize(wprop);
628   gtk_widget_queue_resize(widget);
629   PropIsDraw = false;
630 }
631 
setDrawProp()632 void P2PPad::setDrawProp() {
633   char z[128];
634   snprintf(z,128,_("%s offers a draw."),proto->getRemotePlayer());
635   gtk_widget_set_sensitive(wacc, TRUE);
636   gtk_label_set_text(GTK_LABEL(wprop), z);
637   gtk_widget_queue_resize(wprop);
638   gtk_widget_queue_resize(widget);
639   PropIsDraw = true;
640   global.drawOffered();
641 }
642 
resetDrawProp()643 void P2PPad::resetDrawProp() {
644   clearProposal();
645 }
646 
clearProposal()647 void P2PPad::clearProposal() {
648   char z[64];
649   g_strlcpy(z,_("No proposals left."),64);
650   gtk_widget_set_sensitive(wacc, FALSE);
651   gtk_label_set_text(GTK_LABEL(wprop), z);
652   gtk_widget_queue_resize(wprop);
653   gtk_widget_queue_resize(widget);
654   PropIsDraw = false;
655 }
656 
p2ppad_accept(GtkWidget * w,gpointer data)657 void p2ppad_accept(GtkWidget *w, gpointer data) {
658   P2PPad *me = (P2PPad *) data;
659   if (global.protocol)
660     me->proto->acceptProposal();
661   me->clearProposal();
662 }
663 
p2ppad_propose(GtkWidget * w,gpointer data)664 void p2ppad_propose(GtkWidget *w, gpointer data) {
665   P2PPad *me = (P2PPad *) data;
666   GameProposal g;
667   char z[64];
668   int T,a,b;
669   tstring t;
670   string *p;
671 
672   t.set(gtk_entry_get_text(GTK_ENTRY(me->wtime)));
673   a = 0;
674   while((p=t.token(":"))!=0)
675     a = (60*a) + atoi(p->c_str());
676 
677   b = atoi(gtk_entry_get_text(GTK_ENTRY(me->winc)));
678   g.timecontrol.setIcs(a,b);
679   g.Variant = REGULAR;
680   g.AmWhite = (me->color->getSelection() == 0);
681 
682   if (global.protocol)
683     me->proto->sendProposal(g);
684 }
685 
GameProposal()686 GameProposal::GameProposal() {
687   AmWhite = false;
688   timecontrol.mode = TC_NONE;
689   Variant = REGULAR;
690 }
691 
operator =(GameProposal & g)692 GameProposal GameProposal::operator=(GameProposal &g) {
693   AmWhite     = g.AmWhite;
694   timecontrol = g.timecontrol;
695   Variant     = g.Variant;
696 }
697