1 //------------------------------------------------------------------------------
2 // XSilChess.cpp - X11 version of SilChess
3 //
4 // Copyright (C) 2001-2005,2007-2009 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <Xm/XmAll.h>
26 #include <X11/IntrinsicP.h>
27 #include <SilChess/SilChessRayTracer.h>
28 
29 
30 //==============================================================================
31 //============================== XSilChessWindow ===============================
32 //==============================================================================
33 
34 class XSilChessWindow {
35 
36 public:
37 
38 	XSilChessWindow(XtAppContext app, Widget toplevel, Visual * vsl,
39 	               int vsldepth, Colormap cmap);
40 
41 	~XSilChessWindow();
42 
43 private:
44 
45 	static void HandleCallback(Widget widget, XtPointer client_data,
46 	                           XtPointer call_data);
47 
48 	static void HandleEvent(Widget widget, XtPointer data,
49 	                        XEvent * event, Boolean *);
50 
51 	void HandleCallbackOrEvent(Widget widget,
52 	                           XmAnyCallbackStruct * cbs,
53 	                           XEvent * event);
54 
55 	void MousePress(int x, int y);
56 
57 	void DoSearching();
58 
59 	void UpdateStatusBar();
60 
61 	void UpdateMovesList();
62 
63 	void UpdateView();
64 
65 	void UpdateDepthMenu();
66 
67 	void DoPainting();
68 
69 	void PaintSel();
70 
71 	SilChessMachine * Machine;
72 	int SelX,SelY;
73 	char OverwriteFile[1024];
74 
75 	bool IsSearching,AbortSearching;
76 	bool NeedPainting,IsPainting;
77 	bool HintWanted,HintValid;
78 	SilChessMachine::Move Hint;
79 
80 	XtAppContext App;
81 	Display * Disp;
82 	Visual * Vsl, * DlgVsl;
83 	int VslDepth, DlgVslDepth;
84 	Colormap CMap, DlgCMap;
85 	int PixelSize,RedMask,GreenMask,BlueMask;
86 	Widget TopLevel,MainWin,MainMenu,
87 	       FileMenu,BFileLoad,BFileSave,BFileExit,BFile,
88 	       GameMenu,BGameNew,BGameFlip,BGameUndo,BGameList,BGame,
89 	       DepthMenu,BDepth[SilChessMachine::MAX_SEARCH_DEPTH+1],
90 	       CompMenu,BCompHint,BCompDepth,BComp,HelpMenu,BHelp,
91 	       BHelpAbout,MainForm,StatusFrame,StatusLabel,ViewFrame,
92 	       ViewArea,LoadDialog,SaveDialog,OverwriteDialog,ErrorBox,
93 	       ListDialogPopup,ListDialog,LDClose,LDScroll,LDList,
94 	       AboutDialog;
95 	Dimension ViewWidth,ViewHeight;
96 	Window ViewWin;
97 	GC ViewGC;
98 	SilChessRayTracer RT;
99 
100 	static const char * const AboutText;
101 };
102 
103 
XSilChessWindow(XtAppContext app,Widget toplevel,Visual * vsl,int vsldepth,Colormap cmap)104 XSilChessWindow::XSilChessWindow(XtAppContext app, Widget toplevel,
105                                  Visual * vsl, int vsldepth, Colormap cmap)
106 {
107 	char tmp[512];
108 	Arg al[10];
109 	int i;
110 	XmString xms;
111 
112 	// Initialize member variables
113 	App=app;
114 	TopLevel=toplevel;
115 	Disp=XtDisplay(TopLevel);
116 	Vsl=vsl;
117 	VslDepth=vsldepth;
118 	CMap=cmap;
119 	DlgVsl=DefaultVisual(Disp,XScreenNumberOfScreen(XtScreen(TopLevel)));
120 	DlgVslDepth=DefaultDepth(Disp,XScreenNumberOfScreen(XtScreen(TopLevel)));
121 	DlgCMap=DefaultColormap(Disp,XScreenNumberOfScreen(XtScreen(TopLevel)));
122 	PixelSize=(VslDepth<=8 ? 1 : (VslDepth<=16 ? 2 : 4));
123 	RedMask=Vsl->red_mask;
124 	GreenMask=Vsl->green_mask;
125 	BlueMask=Vsl->blue_mask;
126 	SelX=SelY-1;
127 	IsSearching=false;
128 	AbortSearching=false;
129 	NeedPainting=false;
130 	IsPainting=false;
131 	HintWanted=false;
132 	HintValid=false;
133 
134 	// Create main window
135 	MainWin=XtVaCreateManagedWidget(
136 		"mainWin",xmMainWindowWidgetClass,TopLevel,
137 		(char*)NULL
138 	);
139 
140 	// Create main menu bar
141 	MainMenu=XmCreateMenuBar(MainWin,(char*)"mainMenu",NULL,0);
142 	XtManageChild(MainMenu);
143 
144 	// Create menu item: file
145 	XtSetArg(al[0],XmNvisual,Vsl);
146 	XtSetArg(al[1],XmNdepth,VslDepth);
147 	XtSetArg(al[2],XmNcolormap,CMap);
148 	FileMenu=XmCreatePulldownMenu(MainMenu,(char*)"fileMenu",al,3);
149 	BFile=XtVaCreateManagedWidget(
150 		"file",xmCascadeButtonWidgetClass,MainMenu,
151 		XmNsubMenuId,FileMenu,
152 		(char*)NULL
153 	);
154 
155 	// Create menu item: file/load
156 	BFileLoad=XtVaCreateManagedWidget(
157 		"load",xmPushButtonWidgetClass,FileMenu,
158 		(char*)NULL
159 	);
160 	XtAddCallback(BFileLoad,XmNactivateCallback,HandleCallback,this);
161 
162 	// Create menu item: file/save
163 	BFileSave=XtVaCreateManagedWidget(
164 		"save",xmPushButtonWidgetClass,FileMenu,
165 		(char*)NULL
166 	);
167 	XtAddCallback(BFileSave,XmNactivateCallback,HandleCallback,this);
168 
169 	// Create menu item: file/exit
170 	XtVaCreateManagedWidget(
171 		"separator",xmSeparatorWidgetClass,FileMenu,
172 		(char*)NULL
173 	);
174 	BFileExit=XtVaCreateManagedWidget(
175 		"exit",xmPushButtonWidgetClass,FileMenu,
176 		(char*)NULL
177 	);
178 	XtAddCallback(BFileExit,XmNactivateCallback,HandleCallback,this);
179 
180 	// Create menu item: game
181 	XtSetArg(al[0],XmNvisual,Vsl);
182 	XtSetArg(al[1],XmNdepth,VslDepth);
183 	XtSetArg(al[2],XmNcolormap,CMap);
184 	GameMenu=XmCreatePulldownMenu(MainMenu,(char*)"gameMenu",al,3);
185 	BGame=XtVaCreateManagedWidget(
186 		"game",xmCascadeButtonWidgetClass,MainMenu,
187 		XmNsubMenuId,GameMenu,
188 		(char*)NULL
189 	);
190 
191 	// Create menu item: game/new
192 	BGameNew=XtVaCreateManagedWidget(
193 		"new",xmPushButtonWidgetClass,GameMenu,
194 		(char*)NULL
195 	);
196 	XtAddCallback(BGameNew,XmNactivateCallback,HandleCallback,this);
197 
198 	// Create menu item: game/flip
199 	BGameFlip=XtVaCreateManagedWidget(
200 		"flip",xmPushButtonWidgetClass,GameMenu,
201 		(char*)NULL
202 	);
203 	XtAddCallback(BGameFlip,XmNactivateCallback,HandleCallback,this);
204 
205 	// Create menu item: game/undo
206 	BGameUndo=XtVaCreateManagedWidget(
207 		"undo",xmPushButtonWidgetClass,GameMenu,
208 		(char*)NULL
209 	);
210 	XtAddCallback(BGameUndo,XmNactivateCallback,HandleCallback,this);
211 
212 	// Create menu item: game/list
213 	BGameList=XtVaCreateManagedWidget(
214 		"list",xmPushButtonWidgetClass,GameMenu,
215 		(char*)NULL
216 	);
217 	XtAddCallback(BGameList,XmNactivateCallback,HandleCallback,this);
218 
219 	// Create menu item: computer
220 	XtSetArg(al[0],XmNvisual,Vsl);
221 	XtSetArg(al[1],XmNdepth,VslDepth);
222 	XtSetArg(al[2],XmNcolormap,CMap);
223 	CompMenu=XmCreatePulldownMenu(MainMenu,(char*)"compMenu",al,3);
224 	BComp=XtVaCreateManagedWidget(
225 		"comp",xmCascadeButtonWidgetClass,MainMenu,
226 		XmNsubMenuId,CompMenu,
227 		(char*)NULL
228 	);
229 
230 	// Create menu item: computer/hint
231 	BCompHint=XtVaCreateManagedWidget(
232 		"hint",xmPushButtonWidgetClass,CompMenu,
233 		(char*)NULL
234 	);
235 	XtAddCallback(BCompHint,XmNactivateCallback,HandleCallback,this);
236 
237 	// Create menu item: computer/depth
238 	XtSetArg(al[0],XmNvisual,Vsl);
239 	XtSetArg(al[1],XmNdepth,VslDepth);
240 	XtSetArg(al[2],XmNcolormap,CMap);
241 	XtSetArg(al[3],XmNradioBehavior,True);
242 	DepthMenu=XmCreatePulldownMenu(CompMenu,(char*)"depthMenu",al,4);
243 	BCompDepth=XtVaCreateManagedWidget(
244 		"depth",xmCascadeButtonWidgetClass,CompMenu,
245 		XmNsubMenuId,DepthMenu,
246 		(char*)NULL
247 	);
248 
249 	// Create menu items: computer/depth/1...
250 	for (i=0; i<=SilChessMachine::MAX_SEARCH_DEPTH; i++) {
251 		sprintf(tmp,"%d",i);
252 		BDepth[i]=XtVaCreateManagedWidget(
253 			tmp,xmToggleButtonWidgetClass,DepthMenu,
254 			(char*)NULL
255 		);
256 		XtAddCallback(BDepth[i],XmNvalueChangedCallback,HandleCallback,this);
257 	}
258 
259 	// Create menu item: help
260 	XtSetArg(al[0],XmNvisual,Vsl);
261 	XtSetArg(al[1],XmNdepth,VslDepth);
262 	XtSetArg(al[2],XmNcolormap,CMap);
263 	HelpMenu=XmCreatePulldownMenu(MainMenu,(char*)"helpMenu",al,3);
264 	BHelp=XtVaCreateManagedWidget(
265 		"help",xmCascadeButtonWidgetClass,MainMenu,
266 		XmNsubMenuId,HelpMenu,
267 		(char*)NULL
268 	);
269 	XtVaSetValues(MainMenu,XmNmenuHelpWidget,BHelp,(char*)NULL);
270 
271 	// Create menu item: help/about
272 	BHelpAbout=XtVaCreateManagedWidget(
273 		"about",xmPushButtonWidgetClass,HelpMenu,
274 		(char*)NULL
275 	);
276 	XtAddCallback(BHelpAbout,XmNactivateCallback,HandleCallback,this);
277 
278 	// Create a parent for status bar and view
279 	MainForm=XtVaCreateManagedWidget(
280 		"mainForm",xmFormWidgetClass,MainWin,
281 		(char*)NULL
282 	);
283 
284 	// Create status line
285 	StatusFrame=XtVaCreateManagedWidget(
286 		"statusFrame",xmFrameWidgetClass,MainForm,
287 		XmNleftAttachment,XmATTACH_FORM,
288 		XmNrightAttachment,XmATTACH_FORM,
289 		XmNtopAttachment,XmATTACH_FORM,
290 		(char*)NULL
291 	);
292 	StatusLabel=XtVaCreateManagedWidget(
293 		"statusLabel",xmLabelWidgetClass,StatusFrame,
294 		XmNalignment, XmALIGNMENT_BEGINNING,
295 		(char*)NULL
296 	);
297 
298 	// Create the chess board view
299 	ViewFrame=XtVaCreateManagedWidget(
300 		"viewFrame",xmFrameWidgetClass,MainForm,
301 		XmNtopAttachment,XmATTACH_WIDGET,
302 		XmNtopWidget,StatusFrame,
303 		XmNleftAttachment,XmATTACH_FORM,
304 		XmNrightAttachment,XmATTACH_FORM,
305 		XmNbottomAttachment,XmATTACH_FORM,
306 		(char*)NULL
307 	);
308 	ViewArea=XtVaCreateManagedWidget(
309 		"viewArea",xmDrawingAreaWidgetClass,ViewFrame,
310 		XmNtopAttachment,XmATTACH_FORM,
311 		XmNleftAttachment,XmATTACH_FORM,
312 		XmNrightAttachment,XmATTACH_FORM,
313 		XmNbottomAttachment,XmATTACH_FORM,
314 		(char*)NULL
315 	);
316 	XtAddCallback(ViewArea,XmNexposeCallback,HandleCallback,this);
317 	XtAddCallback(ViewArea,XmNresizeCallback,HandleCallback,this);
318 	XtAddEventHandler(
319 		ViewArea,ButtonPressMask|ButtonMotionMask|ButtonReleaseMask|
320 		StructureNotifyMask,False,HandleEvent,this
321 	);
322 	XtVaGetValues(ViewArea,XmNwidth,&ViewWidth,(char*)NULL);
323 	XtVaGetValues(ViewArea,XmNheight,&ViewHeight,(char*)NULL);
324 	ViewWin=0;
325 	ViewGC=NULL;
326 
327 	// Create dialog: load game
328 	XtSetArg(al[0],XmNvisual,DlgVsl);
329 	XtSetArg(al[1],XmNdepth,DlgVslDepth);
330 	XtSetArg(al[2],XmNcolormap,DlgCMap);
331 	XtSetArg(al[3],XmNautoUnmanage,True);
332 	LoadDialog=XmCreateFileSelectionDialog(TopLevel,(char*)"loadDialog",al,4);
333 	XtAddCallback(LoadDialog,XmNokCallback,HandleCallback,this);
334 	XtUnmanageChild(XmFileSelectionBoxGetChild(LoadDialog,XmDIALOG_HELP_BUTTON));
335 
336 	// Create dialog: save game
337 	XtSetArg(al[0],XmNvisual,DlgVsl);
338 	XtSetArg(al[1],XmNdepth,DlgVslDepth);
339 	XtSetArg(al[2],XmNcolormap,DlgCMap);
340 	XtSetArg(al[3],XmNautoUnmanage, True);
341 	SaveDialog=XmCreateFileSelectionDialog(TopLevel,(char*)"saveDialog",al,4);
342 	XtAddCallback(SaveDialog,XmNokCallback,HandleCallback,this);
343 	XtUnmanageChild(XmFileSelectionBoxGetChild(SaveDialog,XmDIALOG_HELP_BUTTON));
344 
345 	// Create dialog: file exists, overwrite?
346 	XtSetArg(al[0],XmNvisual,DlgVsl);
347 	XtSetArg(al[1],XmNdepth,DlgVslDepth);
348 	XtSetArg(al[2],XmNcolormap,DlgCMap);
349 	XtSetArg(al[3],XmNautoUnmanage, True);
350 	OverwriteDialog=XmCreateWarningDialog(TopLevel,(char*)"overwriteDialog",al,4);
351 	XtUnmanageChild(XmMessageBoxGetChild(OverwriteDialog,XmDIALOG_HELP_BUTTON));
352 	XtAddCallback(OverwriteDialog,XmNokCallback,HandleCallback,this);
353 
354 	// Create dialog: error message
355 	XtSetArg(al[0],XmNvisual,DlgVsl);
356 	XtSetArg(al[1],XmNdepth,DlgVslDepth);
357 	XtSetArg(al[2],XmNcolormap,DlgCMap);
358 	XtSetArg(al[3],XmNautoUnmanage, True);
359 	ErrorBox=XmCreateWarningDialog(TopLevel,(char*)"errorBox",al,4);
360 	XtUnmanageChild(XmMessageBoxGetChild(ErrorBox,XmDIALOG_CANCEL_BUTTON));
361 	XtUnmanageChild(XmMessageBoxGetChild(ErrorBox,XmDIALOG_HELP_BUTTON));
362 
363 	// Create dialog: list of moves
364 	ListDialogPopup=XtVaCreateWidget(
365 		"listDialog_popup",xmDialogShellWidgetClass,TopLevel,
366 		XmNvisual,DlgVsl,
367 		XmNdepth,DlgVslDepth,
368 		XmNcolormap,DlgCMap,
369 		(char*)NULL
370 	);
371 	ListDialog=XtVaCreateWidget(
372 		"listDialog",xmFormWidgetClass,ListDialogPopup,
373 		(char*)NULL
374 	);
375 	LDClose=XtVaCreateManagedWidget(
376 		"close",xmPushButtonWidgetClass,ListDialog,
377 		XmNleftAttachment,XmATTACH_FORM,
378 		XmNrightAttachment,XmATTACH_FORM,
379 		XmNbottomAttachment,XmATTACH_FORM,
380 		(char*)NULL
381 	);
382 	XtAddCallback(LDClose,XmNactivateCallback,HandleCallback,this);
383 	LDScroll=XtVaCreateManagedWidget(
384 		"scroll",xmScrolledWindowWidgetClass,ListDialog,
385 		XmNscrollingPolicy,XmAUTOMATIC,
386 		XmNscrollBarDisplayPolicy,XmAS_NEEDED,
387 		XmNleftAttachment,XmATTACH_FORM,
388 		XmNrightAttachment,XmATTACH_FORM,
389 		XmNtopAttachment,XmATTACH_FORM,
390 		XmNbottomAttachment,XmATTACH_WIDGET,
391 		XmNbottomWidget,LDClose,
392 		(char*)NULL
393 	);
394 	LDList=XtVaCreateManagedWidget(
395 		"list",xmLabelGadgetClass,LDScroll,
396 		XmNalignment,XmALIGNMENT_BEGINNING,
397 		(char*)NULL
398 	);
399 
400 	// Create dialog: about
401 	xms=XmStringCreateLtoR((char*)AboutText,XmFONTLIST_DEFAULT_TAG);
402 	XtSetArg(al[0],XmNvisual,DlgVsl);
403 	XtSetArg(al[1],XmNdepth,DlgVslDepth);
404 	XtSetArg(al[2],XmNcolormap,DlgCMap);
405 	XtSetArg(al[3],XmNautoUnmanage,True);
406 	XtSetArg(al[4],XmNmessageString,xms);
407 	XtSetArg(al[5],XmNmessageAlignment,XmALIGNMENT_CENTER);
408 	AboutDialog=XmCreateMessageDialog(TopLevel,(char*)"aboutDialog",al,6);
409 	XmStringFree(xms);
410 	XtUnmanageChild(XmMessageBoxGetChild(AboutDialog,XmDIALOG_CANCEL_BUTTON));
411 	XtUnmanageChild(XmMessageBoxGetChild(AboutDialog,XmDIALOG_HELP_BUTTON));
412 
413 	// Set main window areas
414 	XmMainWindowSetAreas(MainWin,MainMenu,NULL,NULL,NULL,MainForm);
415 
416 	// Create chess machine
417 	Machine = new SilChessMachine();
418 
419 	// Setup ray tracer
420 	RT.SetViewSize(ViewWidth,ViewHeight);
421 	RT.SetWorld(Machine);
422 
423 	// Update all
424 	UpdateStatusBar();
425 	UpdateMovesList();
426 	UpdateView();
427 	UpdateDepthMenu();
428 }
429 
430 
~XSilChessWindow()431 XSilChessWindow::~XSilChessWindow()
432 {
433 	delete Machine;
434 }
435 
436 
HandleCallback(Widget widget,XtPointer client_data,XtPointer call_data)437 void XSilChessWindow::HandleCallback(Widget widget, XtPointer client_data,
438                                      XtPointer call_data)
439 {
440 	((XSilChessWindow*)client_data)->HandleCallbackOrEvent(
441 		widget,
442 		(XmAnyCallbackStruct*)call_data,
443 		NULL
444 	);
445 }
446 
447 
HandleEvent(Widget widget,XtPointer data,XEvent * event,Boolean *)448 void XSilChessWindow::HandleEvent(Widget widget, XtPointer data,
449                                   XEvent * event, Boolean *)
450 {
451 	((XSilChessWindow*)data)->HandleCallbackOrEvent(
452 		widget,
453 		NULL,
454 		event
455 	);
456 }
457 
458 
HandleCallbackOrEvent(Widget widget,XmAnyCallbackStruct * cbs,XEvent * event)459 void XSilChessWindow::HandleCallbackOrEvent(Widget widget,
460                                             XmAnyCallbackStruct * cbs,
461                                             XEvent * event)
462 {
463 	XGCValues gcval;
464 	const char * file_name;
465 	XmString xms;
466 	char tmp[512];
467 	int i;
468 
469 	if (widget==BFileLoad) {
470 		XtUnmanageChild(LoadDialog);
471 		XmFileSelectionDoSearch(LoadDialog,NULL);
472 		XtManageChild(LoadDialog);
473 	}
474 	else if (widget==BFileSave) {
475 		XtUnmanageChild(SaveDialog);
476 		XmFileSelectionDoSearch(SaveDialog,NULL);
477 		XtManageChild(SaveDialog);
478 	}
479 	else if (widget==BFileExit) {
480 		exit(0);
481 	}
482 	else if (widget==BGameNew) {
483 		AbortSearching=true;
484 		HintWanted=false;
485 		HintValid=false;
486 		Machine->StartNewGame();
487 		RT.SetWorld(Machine);
488 		UpdateStatusBar();
489 		UpdateMovesList();
490 		UpdateView();
491 	}
492 	else if (widget==BGameFlip) {
493 		AbortSearching=true;
494 		HintWanted=false;
495 		HintValid=false;
496 		Machine->SetHumanWhite(!Machine->IsHumanWhite());
497 		RT.SetWorld(Machine);
498 		UpdateStatusBar();
499 		UpdateView();
500 	}
501 	else if (widget==BGameUndo) {
502 		AbortSearching=true;
503 		HintWanted=false;
504 		HintValid=false;
505 		Machine->UndoMove();
506 		if (!Machine->IsHumanOn()) Machine->UndoMove();
507 		RT.SetWorld(Machine);
508 		UpdateStatusBar();
509 		UpdateMovesList();
510 		UpdateView();
511 	}
512 	else if (widget==BGameList) {
513 		XtUnmanageChild(ListDialog);
514 		XtManageChild(ListDialog);
515 	}
516 	else if (widget==LoadDialog) {
517 		AbortSearching=true;
518 		HintWanted=false;
519 		HintValid=false;
520 		file_name=XmTextGetString(
521 			XmFileSelectionBoxGetChild(widget,XmDIALOG_TEXT)
522 		);
523 		if (!Machine->Load(file_name)) {
524 			XtUnmanageChild(ErrorBox);
525 			sprintf(tmp,"Failed to load '%s'",file_name);
526 			xms=XmStringCreateSimple(tmp);
527 			XtVaSetValues(ErrorBox,XmNmessageString,xms,(char*)NULL);
528 			XmStringFree(xms);
529 			XtManageChild(ErrorBox);
530 		}
531 		RT.SetWorld(Machine);
532 		UpdateStatusBar();
533 		UpdateMovesList();
534 		UpdateView();
535 		UpdateDepthMenu();
536 	}
537 	else if (widget==SaveDialog) {
538 		file_name=XmTextGetString(
539 			XmFileSelectionBoxGetChild(widget,XmDIALOG_TEXT)
540 		);
541 		if (access(file_name,F_OK)!=-1) {
542 			XtUnmanageChild(OverwriteDialog);
543 			sprintf(tmp,"OK to overwrite '%s'?",file_name);
544 			xms=XmStringCreateSimple(tmp);
545 			XtVaSetValues(OverwriteDialog,XmNmessageString,xms,(char*)NULL);
546 			XmStringFree(xms);
547 			XtManageChild(OverwriteDialog);
548 			strcpy(OverwriteFile,file_name);
549 		}
550 		else if (!Machine->Save(file_name)) {
551 			XtUnmanageChild(ErrorBox);
552 			sprintf(tmp,"Failed to save '%s'",file_name);
553 			xms=XmStringCreateSimple(tmp);
554 			XtVaSetValues(ErrorBox,XmNmessageString,xms,(char*)NULL);
555 			XmStringFree(xms);
556 			XtManageChild(ErrorBox);
557 		}
558 	}
559 	else if (widget==OverwriteDialog) {
560 		file_name=OverwriteFile;
561 		if (!Machine->Save(file_name)) {
562 			XtUnmanageChild(ErrorBox);
563 			sprintf(tmp,"Failed to save '%s'",file_name);
564 			xms=XmStringCreateSimple(tmp);
565 			XtVaSetValues(ErrorBox,XmNmessageString,xms,(char*)NULL);
566 			XmStringFree(xms);
567 			XtManageChild(ErrorBox);
568 		}
569 	}
570 	else if (widget==ViewArea) {
571 		if (cbs!=NULL && cbs->reason==XmCR_EXPOSE &&
572 		    cbs->event && cbs->event->xexpose.count==0) {
573 			UpdateView();
574 		}
575 		else if (cbs!=NULL && cbs->reason==XmCR_RESIZE) {
576 			XtVaGetValues(ViewArea,XmNwidth,&ViewWidth,(char*)NULL);
577 			XtVaGetValues(ViewArea,XmNheight,&ViewHeight,(char*)NULL);
578 			RT.SetViewSize(ViewWidth,ViewHeight);
579 			UpdateView();
580 		}
581 		else if (event!=NULL && event->type==ButtonPress) {
582 			MousePress(event->xbutton.x,event->xbutton.y);
583 		}
584 		else if (event!=NULL && event->type==MapNotify && !ViewWin) {
585 			ViewWin=XtWindow(ViewArea);
586 			ViewGC=XtGetGC(ViewArea,0,&gcval);
587 		}
588 	}
589 	else if (widget==BCompHint) {
590 		AbortSearching=true;
591 		HintWanted=true;
592 		HintValid=false;
593 		UpdateStatusBar();
594 	}
595 	else if (widget==BHelpAbout) {
596 		XtUnmanageChild(AboutDialog);
597 		XtManageChild(AboutDialog);
598 	}
599 	else if (widget==LDClose) {
600 		XtUnmanageChild(ListDialog);
601 	}
602 	else {
603 		for (i=0; i<=SilChessMachine::MAX_SEARCH_DEPTH; i++) {
604 			if (widget==BDepth[i] && cbs!=NULL) {
605 				if (((XmToggleButtonCallbackStruct*)cbs)->set) {
606 					AbortSearching=true;
607 					Machine->SetSearchDepth(i);
608 					UpdateStatusBar();
609 				}
610 			}
611 		}
612 	}
613 	do {
614 		DoPainting();
615 		DoSearching();
616 	} while(NeedPainting && !IsPainting && ViewWin);
617 }
618 
619 
MousePress(int x,int y)620 void XSilChessWindow::MousePress(int x, int y)
621 {
622 	int i;
623 	SilChessMachine::Move m;
624 
625 	RT.View2Board(x,y,&x,&y);
626 	if (x<0 || y<0 || x>7 || y>7 || (x==SelX && y==SelY)) {
627 		if (SelX!=-1 || SelY!=-1) {
628 			SelX=SelY=-1;
629 			UpdateView();
630 		}
631 		return;
632 	}
633 	i=Machine->GetField(x,y);
634 	if (i!=0 && ((i<7) == Machine->IsWhiteOn())) {
635 		if (SelX!=x || SelY!=y) {
636 			if (SelX==-1 || SelY==-1) {
637 				SelX=x;
638 				SelY=y;
639 				PaintSel();
640 			}
641 			else {
642 				SelX=x;
643 				SelY=y;
644 				UpdateView();
645 			}
646 		}
647 		return;
648 	}
649 	if (SelX!=-1 && SelY!=-1) {
650 		m.X1=SelX;
651 		m.Y1=SelY;
652 		m.X2=x;
653 		m.Y2=y;
654 		if (Machine->IsLegalMove(m)) {
655 			AbortSearching=true;
656 			HintWanted=false;
657 			HintValid=false;
658 			Machine->DoMove(m);
659 			SelX=SelY=-1;
660 			RT.SetWorld(Machine);
661 			UpdateStatusBar();
662 			UpdateMovesList();
663 			UpdateView();
664 		}
665 		else {
666 			SelX=SelY=-1;
667 			UpdateStatusBar();
668 			UpdateView();
669 		}
670 	}
671 }
672 
673 
DoSearching()674 void XSilChessWindow::DoSearching()
675 {
676 	XEvent e;
677 	SilChessMachine::Move m;
678 	bool res;
679 
680 	if (IsPainting) return;
681 	if (IsSearching) return;
682 	IsSearching=true;
683 	while ((!Machine->IsHumanOn() || HintWanted) &&
684 	       !Machine->IsMate() && !Machine->IsDraw() && !Machine->IsEndless()) {
685 		AbortSearching=false;
686 		Machine->StartSearching();
687 		for (;;) {
688 			if (Machine->ContinueSearching()) break;
689 			while (XtAppPending(App)) {
690 				XtAppNextEvent(App,&e);
691 				XtDispatchEvent(&e);
692 			}
693 			if (AbortSearching) break;
694 		}
695 		res=Machine->EndSearching(&m);
696 		if (!AbortSearching && res) {
697 			if (!Machine->IsHumanOn()) {
698 				Machine->DoMove(m);
699 				RT.SetWorld(Machine);
700 				UpdateStatusBar();
701 				UpdateMovesList();
702 				UpdateView();
703 			}
704 			else if (HintWanted) {
705 				HintWanted=false;
706 				Hint=m;
707 				HintValid=true;
708 				UpdateStatusBar();
709 			}
710 		}
711 	}
712 	IsSearching=false;
713 }
714 
715 
UpdateStatusBar()716 void XSilChessWindow::UpdateStatusBar()
717 {
718 	XmString xms;
719 	char tmp[512];
720 
721 	tmp[0]=0;
722 	if (Machine->GetMoveCount()>0) {
723 		Machine->GetMove(Machine->GetMoveCount()-1).ToString(tmp+strlen(tmp));
724 		sprintf(tmp+strlen(tmp)," <%d>  ",Machine->GetValue());
725 	}
726 	if (Machine->IsMate()) strcat(tmp,"MATE!");
727 	else if (Machine->IsDraw()) strcat(tmp,"DRAW!");
728 	else if (Machine->IsEndless()) strcat(tmp,"ENDLESS!");
729 	else {
730 		if (Machine->IsCheck()) strcat(tmp,"check! ");
731 		if (!Machine->IsHumanOn()) {
732 			sprintf(tmp+strlen(tmp),"searching (%d)...",Machine->GetSearchDepth());
733 		}
734 		else if (HintWanted) {
735 			sprintf(tmp+strlen(tmp),"searching hint (%d)...",Machine->GetSearchDepth());
736 		}
737 		else {
738 			if (HintValid) {
739 				strcat(tmp,"hint: ");
740 				Hint.ToString(tmp+strlen(tmp));
741 				strcat(tmp,", ");
742 			}
743 			strcat(tmp,"your move? ");
744 		}
745 	}
746 	xms=XmStringCreateLtoR(tmp,XmFONTLIST_DEFAULT_TAG);
747 	XtVaSetValues(StatusLabel,XmNlabelString,xms,(char*)NULL);
748 	XmStringFree(xms);
749 }
750 
751 
UpdateMovesList()752 void XSilChessWindow::UpdateMovesList()
753 {
754 	XmString xms;
755 	Widget clpwin,scrbar;
756 	char * buf, * p;
757 	int i,smax,ssiz;
758 	Position x,y;
759 	Dimension h,hc;
760 
761 	buf=(char*)malloc(65536);
762 	p=buf;
763 	for (i=0; i<Machine->GetMoveCount(); i++) {
764 		if ((i&1)==0) p+=sprintf(p,"%03d",i/2+1);
765 		*p++=' ';
766 		Machine->GetMove(i).ToString(p);
767 		p+=4;
768 		if ((i&1)!=0 || i==Machine->GetMoveCount()-1) *p++='\n';
769 	}
770 	*p=0;
771 	if (p==buf) strcpy(buf,"<empty>");
772 	xms=XmStringCreateLtoR(buf,XmFONTLIST_DEFAULT_TAG);
773 	free(buf);
774 
775 	XtVaSetValues(LDList,XmNlabelString,xms,(char*)NULL);
776 	XmStringFree(xms);
777 
778 	// Make last entry visible
779 	clpwin=NULL;
780 	XtVaGetValues(LDScroll,XmNclipWindow,&clpwin,(char*)NULL);
781 	if (clpwin) {
782 		XtVaGetValues(LDList,XmNx,&x,XmNy,&y,XmNheight,&h,(char*)NULL);
783 		XtVaGetValues(clpwin,XmNheight,&hc,(char*)NULL);
784 		if (((Position)hc-h)<y) {
785 			XtMoveWidget(LDList,x,hc-h);
786 			scrbar=NULL;
787 			XtVaGetValues(LDScroll,XmNverticalScrollBar,&scrbar,(char*)NULL);
788 			if (scrbar) {
789 				XtVaGetValues(scrbar,XmNmaximum,&smax,XmNsliderSize,&ssiz,(char*)NULL);
790 				XtVaSetValues(scrbar,XmNvalue,smax-ssiz,(char*)NULL);
791 			}
792 		}
793 	}
794 }
795 
796 
UpdateView()797 void XSilChessWindow::UpdateView()
798 {
799 	NeedPainting=true;
800 }
801 
802 
UpdateDepthMenu()803 void XSilChessWindow::UpdateDepthMenu()
804 {
805 	int i;
806 
807 	for (i=0; i<SilChessMachine::MAX_SEARCH_DEPTH; i++) {
808 		XtVaSetValues(
809 			BDepth[i],
810 			XmNset,
811 			Machine->GetSearchDepth()==i ? True : False,
812 			(char*)NULL
813 		);
814 	}
815 }
816 
817 
DoPainting()818 void XSilChessWindow::DoPainting()
819 {
820 	XEvent e;
821 	char * data;
822 	XImage * img;
823 	int i,y,msk;
824 
825 	if (IsPainting) return;
826 	if (!ViewWin) return;
827 	IsPainting=true;
828 	y=0;
829 	while (NeedPainting) {
830 		NeedPainting=false;
831 		RT.SetWorld(Machine);
832 		data=(char*)malloc(PixelSize*ViewWidth+1000000);
833 		img=XCreateImage(Disp,Vsl,VslDepth,ZPixmap,0,data,
834 		                 ViewWidth,1,8*PixelSize,0);
835 		for (msk=0x3ff; msk<ViewHeight-1; msk=(msk<<1)|1);
836 		for (i=0, y%=ViewHeight; i<ViewHeight; i++) {
837 			while (XtAppPending(App)!=0) {
838 				XtAppNextEvent(App,&e);
839 				XtDispatchEvent(&e);
840 			}
841 			if (NeedPainting) break;
842 			RT.RenderScanline(y,data,PixelSize,RedMask,GreenMask,BlueMask);
843 			XPutImage(Disp,ViewWin,ViewGC,img,0,0,0,y,ViewWidth,1);
844 			if (i==ViewHeight-1 || (i&7)==0) PaintSel();
845 			do { y=(y+269779)&msk; } while (y>=ViewHeight);
846 		}
847 		PaintSel();
848 		XFree(img);
849 		free(data);
850 	}
851 	IsPainting=false;
852 }
853 
854 
PaintSel()855 void XSilChessWindow::PaintSel()
856 {
857 	int i,tx,ty;
858 	int mvx[4],mvy[4];
859 
860 	if (SelX<0 || SelY<0 || !ViewWin) return;
861 	RT.Board2View(SelX-0.5,SelY-0.5,&(mvx[0]),&(mvy[0]));
862 	RT.Board2View(SelX-0.5,SelY+0.5,&(mvx[1]),&(mvy[1]));
863 	RT.Board2View(SelX+0.5,SelY+0.5,&(mvx[2]),&(mvy[2]));
864 	RT.Board2View(SelX+0.5,SelY-0.5,&(mvx[3]),&(mvy[3]));
865 	XSetForeground(Disp,ViewGC,0);
866 	for (i=0; i<4; i++) {
867 		tx=mvx[(i+1)&3]+(mvx[i]-mvx[(i+1)&3])/6;
868 		ty=mvy[(i+1)&3]+(mvy[i]-mvy[(i+1)&3])/6;
869 		XDrawLine(Disp,ViewWin,ViewGC,tx,ty,mvx[(i+1)&3],mvy[(i+1)&3]);
870 		tx=mvx[i]+(mvx[(i+1)&3]-mvx[i])/6;
871 		ty=mvy[i]+(mvy[(i+1)&3]-mvy[i])/6;
872 		XDrawLine(Disp,ViewWin,ViewGC,mvx[i],mvy[i],tx,ty);
873 	}
874 }
875 
876 
877 const char * const XSilChessWindow::AboutText=
878 	"XSilChess 3.1\n\n"
879 	"Copyright (C) 2001-2009 Oliver Hamann\n"
880 ;
881 
882 
883 //==============================================================================
884 //============================= SCDefaultResources =============================
885 //==============================================================================
886 
887 static const char * SCDefaultResources[]= {
888 	"XSilChess*.background: #c0c0c0",
889 	"XSilChess*.foreground: black",
890 	"XSilChess.title: XSilChess",
891 	"XSilChess.minWidth: 190",
892 	"XSilChess.minHeight: 176",
893 	"XSilChess.mainWin.width: 460",
894 	"XSilChess.mainWin.height: 350",
895 	"XSilChess.mainWin.mainMenu.shadowThickness: 1",
896 	"XSilChess.mainWin.mainMenu.file.labelString: File",
897 	"XSilChess.mainWin.mainMenu.file.mnemonic: F",
898 	"XSilChess.mainWin.mainMenu*.fileMenu.load.labelString: Load...",
899 	"XSilChess.mainWin.mainMenu*.fileMenu.load.mnemonic: L",
900 	"XSilChess.mainWin.mainMenu*.fileMenu.load.accelerator: Ctrl<Key>L",
901 	"XSilChess.mainWin.mainMenu*.fileMenu.load.acceleratorText: Ctrl+L",
902 	"XSilChess.mainWin.mainMenu*.fileMenu.save.labelString: Save...",
903 	"XSilChess.mainWin.mainMenu*.fileMenu.save.mnemonic: S",
904 	"XSilChess.mainWin.mainMenu*.fileMenu.save.accelerator: Ctrl<Key>S",
905 	"XSilChess.mainWin.mainMenu*.fileMenu.save.acceleratorText: Ctrl+S",
906 	"XSilChess.mainWin.mainMenu*.fileMenu.exit.labelString: Exit",
907 	"XSilChess.mainWin.mainMenu*.fileMenu.exit.mnemonic: x",
908 	"XSilChess.mainWin.mainMenu*.fileMenu.exit.accelerator: Ctrl<Key>Q",
909 	"XSilChess.mainWin.mainMenu*.fileMenu.exit.acceleratorText: Ctrl+Q",
910 	"XSilChess.mainWin.mainMenu.game.labelString: Game",
911 	"XSilChess.mainWin.mainMenu.game.mnemonic: G",
912 	"XSilChess.mainWin.mainMenu*.gameMenu.new.labelString: New Game",
913 	"XSilChess.mainWin.mainMenu*.gameMenu.new.mnemonic: N",
914 	"XSilChess.mainWin.mainMenu*.gameMenu.new.accelerator: Ctrl<Key>N",
915 	"XSilChess.mainWin.mainMenu*.gameMenu.new.acceleratorText: Ctrl+N",
916 	"XSilChess.mainWin.mainMenu*.gameMenu.flip.labelString: Flip Sides",
917 	"XSilChess.mainWin.mainMenu*.gameMenu.flip.mnemonic: F",
918 	"XSilChess.mainWin.mainMenu*.gameMenu.flip.accelerator: Ctrl<Key>F",
919 	"XSilChess.mainWin.mainMenu*.gameMenu.flip.acceleratorText: Ctrl+F",
920 	"XSilChess.mainWin.mainMenu*.gameMenu.undo.labelString: Undo Move",
921 	"XSilChess.mainWin.mainMenu*.gameMenu.undo.mnemonic: U",
922 	"XSilChess.mainWin.mainMenu*.gameMenu.undo.accelerator: Ctrl<Key>Z",
923 	"XSilChess.mainWin.mainMenu*.gameMenu.undo.acceleratorText: Ctrl+Z",
924 	"XSilChess.mainWin.mainMenu*.gameMenu.list.labelString: Show Moves...",
925 	"XSilChess.mainWin.mainMenu*.gameMenu.list.mnemonic: M",
926 	"XSilChess.mainWin.mainMenu*.gameMenu.list.accelerator: Ctrl<Key>M",
927 	"XSilChess.mainWin.mainMenu*.gameMenu.list.acceleratorText: Ctrl+M",
928 	"XSilChess.mainWin.mainMenu.comp.labelString: Computer",
929 	"XSilChess.mainWin.mainMenu.comp.mnemonic: C",
930 	"XSilChess.mainWin.mainMenu*.compMenu.hint.labelString: Give Hint",
931 	"XSilChess.mainWin.mainMenu*.compMenu.hint.mnemonic: H",
932 	"XSilChess.mainWin.mainMenu*.compMenu.hint.accelerator: Ctrl<Key>H",
933 	"XSilChess.mainWin.mainMenu*.compMenu.hint.acceleratorText: Ctrl+H",
934 	"XSilChess.mainWin.mainMenu*.compMenu.depth.labelString: Search Depth",
935 	"XSilChess.mainWin.mainMenu*.compMenu.depth.mnemonic: D",
936 	"XSilChess.mainWin.mainMenu.help.labelString: Help",
937 	"XSilChess.mainWin.mainMenu.help.mnemonic: H",
938 	"XSilChess.mainWin.mainMenu*.helpMenu.about.labelString: About...",
939 	"XSilChess.mainWin.mainMenu*.helpMenu.about.mnemonic: A",
940 	"XSilChess.mainWin.mainForm.statusFrame.shadowType: SHADOW_IN",
941 	"XSilChess.mainWin.mainForm.statusFrame.shadowThickness: 1",
942 	"XSilChess.mainWin.mainForm.viewFrame.shadowType: SHADOW_IN",
943 	"XSilChess.mainWin.mainForm.viewFrame.shadowThickness: 0",
944 	"XSilChess.mainWin.mainForm.viewFrame.viewArea.background: #4d3319",
945 	"XSilChess.loadDialog_popup.loadDialog.dialogTitle: Load Game",
946 	"XSilChess.loadDialog_popup.loadDialog.pattern: *.silchess",
947 	"XSilChess.saveDialog_popup.saveDialog.dialogTitle: Save Game",
948 	"XSilChess.saveDialog_popup.saveDialog.pattern: *.silchess",
949 	"XSilChess.overwriteDialog_popup.overwriteDialog.dialogTitle: File exists",
950 	"XSilChess.errorBox_popup.errorBox.dialogTitle: Error",
951 	"XSilChess.listDialog_popup.title: List of Moves",
952 	"XSilChess.listDialog_popup.minWidth: 96",
953 	"XSilChess.listDialog_popup.minHeight: 96",
954 	"XSilChess.listDialog_popup.listDialog.close.labelString: Close",
955 	"XSilChess.aboutDialog_popup.aboutDialog.dialogTitle: About",
956 	NULL
957 };
958 
959 
960 //==============================================================================
961 //==================================== main ====================================
962 //==============================================================================
963 
main(int argc,char * argv[])964 int main(int argc, char * argv[])
965 {
966 	XtAppContext App;
967 	XSilChessWindow * MainWindow;
968 	Widget TopLevel;
969 	Colormap colormap;
970 	Visual * visual;
971 	XVisualInfo * vil;
972 	XVisualInfo vit;
973 	Display * display;
974 	int vic,i,depth;
975 
976 	TopLevel=XtVaAppInitialize(
977 		&App,"XSilChess",NULL,0,&argc,argv,
978 		(char**)SCDefaultResources,(char*)NULL
979 	);
980 	display=XtDisplay(TopLevel);
981 	memset(&vit,0,sizeof(vit));
982 	vit.screen=XScreenNumberOfScreen(XtScreen(TopLevel));
983 	vil=XGetVisualInfo(display,VisualScreenMask,&vit,&vic);
984 	visual=NULL;
985 	depth=0;
986 	for (i=0; i<vic; i++) {
987 		if (vil[i].c_class==TrueColor) {
988 			visual=vil[i].visual;
989 			depth=vil[i].depth;
990 			break;
991 		}
992 	}
993 	if (visual==NULL) {
994 		fprintf(stderr,"ERROR: no true color visual available\n");
995 		return 1;
996 	}
997 	colormap=XCreateColormap(display,DefaultRootWindow(display),visual,AllocNone);
998 	XtVaSetValues(
999 		TopLevel,
1000 		XmNvisual,visual,
1001 		XmNdepth,depth,
1002 		XmNcolormap,colormap,
1003 		(char*)NULL
1004 	);
1005 	MainWindow=new XSilChessWindow(App,TopLevel,visual,depth,colormap);
1006 	XtRealizeWidget(TopLevel);
1007 	XtAppMainLoop(App);
1008 	delete MainWindow;
1009 	return 0;
1010 }
1011