This patch fixes different issues to improve scid's responsiveness.
To test it open a "tree window", an "analysis engine" and move very fast
in a game with keyboard arrows or mouse wheel (if you always have a tree
window and an engine running, like me, you're gonna love this patch).
Please try this accurately and commit if no bugs will be reported.
Bye,
Fulvio
Explanations:
*** main.tcl ***
proc updateBoard asks other windows to refresh only after processing all
queued events.
This means that if you move so fast (or your computer is so busy) that
position change while repainting the main board, other windows will be
informed only of the last change.
*** analysis.tcl ***
Change the logic for uci engines.
Instead of waiting for bestmove and the 0.3s delay use uci command
"isready" to ask the engine to notify when it's ready.
This means that scid no longer wait for chess engines.
1) proc updateAnalysis ask the engine to stop and be ready for a new
command.
2) The new command is scheduled to be executed after engine response
"readyok"
3) proc updateAnalysis returns immediately
If updateAnalysis is re-called before the engine was ready, the
scheduled command will be replaced by the new one
*** file.tcl ***
fixes a bug that leaves the cursor busy in case of error (for example if
your try to open as tree an already opened base)
partially fix a bug that change the current base when opening another
base as tree: the current base is still changed but it's switched back
before opening the "tree window"
partially fix a bug that locks the base only after creating the "tree
window": if you close the window before completion now the base is
correctly closed too (actually this corner case still return an error:
there are other bugs in tree.tcl that i will solve with the new best
game window)
*** uci.tcl ***
removed a bizarre "update idletasks" call
fixes a possible skip of return if $analysis(waitForBestMove$n) == 0
*** tree.tcl ***
removed "wm stackorder" that causes problems on Mac OS X and is no
longer needed.
multiple tree windows will be refreshed in reverse order: last opened
base as tree will be refreshed first.
removed busy cursor: tree resfresh no longer requires to wait completion.
diff --git a/tcl/file.tcl b/tcl/file.tcl
index de90dc7..b1896d7 100644
--- a/tcl/file.tcl
+++ b/tcl/file.tcl
@@ -367,6 +367,7 @@ proc ::file::openBaseAsTree { { fName "" } } {
if {[file extension $fName] == ".si4"} {
set fName [file rootname $fName]
if {[catch {openBase $fName} result]} {
+ unbusyCursor .
set err 1
tk_messageBox -icon warning -type ok -parent . -title "Scid: Error opening file" -message $result
return
@@ -379,6 +380,7 @@ proc ::file::openBaseAsTree { { fName "" } } {
set result "This file is not readable."
if {(![file readable $fName]) || \
[catch {sc_base create $fName true} result]} {
+ unbusyCursor .
set err 1
tk_messageBox -icon warning -type ok -parent . -title "Scid: Error opening file" -message $result
return
@@ -390,8 +392,8 @@ proc ::file::openBaseAsTree { { fName "" } } {
}
unbusyCursor .
- ::tree::make [sc_base current]
- .treeWin[sc_base current].buttons.lock invoke
+ set new_base [sc_base current]
::file::SwitchToBase $current
+ ::tree::make $new_base 1
}
diff --git a/tcl/main.tcl b/tcl/main.tcl
index e4716b2..1fccd42 100644
--- a/tcl/main.tcl
+++ b/tcl/main.tcl
@@ -832,19 +832,33 @@ proc flipBoardForPlayerNames {namelist {board .main.board}} {
# If a parameter "-animate" is specified, board changes are animated.
#
proc updateBoard {args} {
- global boardSize gameInfo
+ global boardSize
set pgnNeedsUpdate 0
set animate 0
foreach arg $args {
if {! [string compare $arg "-pgn"]} { set pgnNeedsUpdate 1 }
if {! [string compare $arg "-animate"]} { set animate 1 }
}
-
+
+ if {$pgnNeedsUpdate} { ::pgn::Refresh $pgnNeedsUpdate }
+
::board::resize .main.board $boardSize
::board::setmarks .main.board [sc_pos getComment]
::board::update .main.board [sc_pos board] $animate
-
- # Update the status of each navigation button:
+
+ after cancel updateNavButtons
+ after cancel notifyPosChange
+
+ update idletasks
+
+ after idle updateNavButtons
+ after idle notifyPosChange
+}
+
+# updateNavButtons:
+# Update the status of each navigation button
+#
+proc updateNavButtons {} {
if {[sc_pos isAt start]} {
.main.fbutton.button.start configure -state disabled
} else { .main.fbutton.button.start configure -state normal }
@@ -884,12 +898,26 @@ proc updateBoard {args} {
} else {
.main.fbutton.button.exitVar configure -state normal
}
-
- if {![sc_base inUse] || $::trialMode || [sc_base isReadOnly]} {
- .main.tb.save configure -state disabled
+
+ wm withdraw .tooltip
+ set comment [sc_pos getComment]
+ # remove technical comments, notify only human readable ones
+ regsub -all {\[%.*\]} $comment {} comment
+ if {$comment != ""} {
+ .main.fbutton.button.comment configure -image comment_avail -relief flat
+ ::utils::tooltip::Set .main.fbutton.button.comment $comment
} else {
- .main.tb.save configure -state normal
+ .main.fbutton.button.comment configure -image comment_unavail -relief flat
+ ::utils::tooltip::UnSet .main.fbutton.button.comment
}
+}
+
+# updateGameInfo:
+# Update the game status window .main.gameInfo
+#
+proc updateGameInfo {} {
+ global gameInfo
+
.main.gameInfo configure -state normal
.main.gameInfo delete 0.0 end
::htext::display .main.gameInfo [sc_game info -hide $gameInfo(hideNextMove) \
@@ -905,36 +933,33 @@ proc updateBoard {args} {
}
.main.gameInfo configure -state disabled
updatePlayerPhotos
+}
+
+# notifyPosChange:
+# Notify other windows of current position changes
+#
+proc notifyPosChange {} {
+ if {![sc_base inUse] || $::trialMode || [sc_base isReadOnly]} {
+ .main.tb.save configure -state disabled
+ } else {
+ .main.tb.save configure -state normal
+ }
+
+ if {$::showGameInfo} { updateGameInfo }
+ updateAnalysis 1
+ updateAnalysis 2
updateEpdWins
- if {[winfo exists .analysisWin1]} { updateAnalysis 1 }
- if {[winfo exists .analysisWin2]} { updateAnalysis 2 }
- # if {[winfo exists .treeWin]} { ::tree::refresh }
- ::tree::refresh
- if {[winfo exists .commentWin]} { ::commenteditor::Refresh }
- if {[::tb::isopen]} { ::tb::results }
+ ::commenteditor::Refresh
+ ::tb::results
updateMenuStates
moveEntry_Clear
updateStatusBar
-
- update idletasks
-
if {[winfo exists .twinchecker]} { updateTwinChecker }
- if {[winfo exists .pgnWin]} { ::pgn::Refresh $pgnNeedsUpdate }
+ ::pgn::Refresh
if {[winfo exists .bookWin]} { ::book::refresh }
if {[winfo exists .bookTuningWin]} { ::book::refreshTuning }
- if {[winfo exists .noveltyWin]} { updateNoveltyWin }
-
- wm withdraw .tooltip
- set comment [sc_pos getComment]
- # remove technical comments, notify only human readable ones
- regsub -all {\[%.*\]} $comment {} comment
- if {$comment != ""} {
- .main.fbutton.button.comment configure -image comment_avail -relief flat
- ::utils::tooltip::Set .main.fbutton.button.comment $comment
- } else {
- .main.fbutton.button.comment configure -image comment_unavail -relief flat
- ::utils::tooltip::UnSet .main.fbutton.button.comment
- }
+ updateNoveltyWin
+ ::tree::refresh
}
# Set up player photos:
diff --git a/tcl/start.tcl b/tcl/start.tcl
index 682587f..f72a18a 100644
--- a/tcl/start.tcl
+++ b/tcl/start.tcl
@@ -976,6 +976,7 @@ proc toggleGameInfo {} {
} else {
grid forget .main.gameInfoFrame
}
+ updateGameInfo
update idletasks
resizeMainBoard
}
diff --git a/tcl/tools/analysis.tcl b/tcl/tools/analysis.tcl
index 487260b..ed7352f 100644
--- a/tcl/tools/analysis.tcl
+++ b/tcl/tools/analysis.tcl
@@ -1609,6 +1609,11 @@ proc destroyAnalysisWin {{n 1}} {
if { $n == 1 && $annotateMode } {
toggleAutoplay
}
+
+ # Cancel scheduled commands
+ if {$analysis(after$n) != ""} {
+ after cancel $analysis(after$n)
+ }
# Check the pipe is not already closed:
if {$analysis(pipe$n) == ""} {
@@ -2026,28 +2031,20 @@ proc changePVSize { n } {
$h configure -state disabled
set analysis(lastHistory$n) {}
}
- if { $analysis(uci$n) } {
- # if the UCI engine was analysing, we have to stop/restart it to take into acount the new multiPV option
- if {$analysis(analyzeMode$n)} {
- # Normally the best move condition should still stand
- # Meaning that we will stop the engine here and wait for
- # the bestmove reply (which is handled in uci.tcl)
- if { $analysis(waitForBestMove$n) } {
- sendToEngine $n "stop"
- vwait analysis(waitForBestMove$n)
- }
- sendToEngine $n "setoption name MultiPV value $analysis(multiPVCount$n)"
- sendToEngine $n "position fen [sc_pos fen]"
- # Although we go infinite, some engines do a bestmove announcement
- # and go idle when they see a forced mate, before we stop them
- # Let's anticipate this.
- if { [expr { [string index [sc_game info previousMoveNT] end] != "#"}] } {
- set analysis(waitForBestMove$n) 1
- }
- sendToEngine $n "go infinite"
- } else {
- sendToEngine $n "setoption name MultiPV value $analysis(multiPVCount$n)"
- }
+ if { ! $analysis(uci$n) } { return }
+
+ # if the UCI engine was analysing, stop and restart
+ if {$analysis(analyzeMode$n)} {
+ stopAnalyzeMode $n
+ set analysis(waitForReadyOk$n) 1
+ sendToEngine $n "isready"
+ set dont_stuck [ after 60000 "set ::analysis(waitForReadyOk$n) 0" ]
+ vwait analysis(waitForReadyOk$n)
+ after cancel $dont_stuck
+ sendToEngine $n "setoption name MultiPV value $analysis(multiPVCount$n)"
+ startAnalyzeMode $n
+ } else {
+ sendToEngine $n "setoption name MultiPV value $analysis(multiPVCount$n)"
}
}
################################################################################
@@ -2607,21 +2604,7 @@ proc startAnalyzeMode {{n 1} {force 0}} {
if {$analysis(analyzeMode$n) && ! $force } { return }
set analysis(analyzeMode$n) 1
if { $analysis(uci$n) } {
- set analysis(waitForReadyOk$n) 1
- sendToEngine $n "isready"
- vwait analysis(waitForReadyOk$n)
- sendToEngine $n "position fen [sc_pos fen]"
- # Although we go infinite, some engines do a bestmove announcement
- # and go idle when they see a forced mate, before we stop them
- # Let's anticipate this.
- if { [expr { [string index [sc_game info previousMoveNT] end] != "#" }] } {
- set analysis(waitForBestMove$n) 1
- } else {
- set analysis(waitForBestMove$n) 0
- }
- sendToEngine $n "go infinite"
- set analysis(fen$n) [sc_pos fen]
- set analysis(maxmovenumber$n) 0
+ updateAnalysis $n
} else {
if {$analysis(has_setboard$n)} {
sendToEngine $n "setboard [sc_pos fen]"
@@ -2641,10 +2624,15 @@ proc stopAnalyzeMode { {n 1} } {
if {! $analysis(analyzeMode$n)} { return }
set analysis(analyzeMode$n) 0
if { $analysis(uci$n) } {
+ if {$analysis(after$n) != ""} {
+ after cancel $analysis(after$n)
+ set analysis(after$n) ""
+ }
sendToEngine $n "stop"
} else {
sendToEngine $n "exit"
}
+ set analysis(fen$n) {}
}
################################################################################
# toggleLockEngine
@@ -2654,16 +2642,9 @@ proc toggleLockEngine {n} {
global analysis
if { $analysis(lockEngine$n) } {
set state disabled
- set analysis(lockN$n) [sc_pos moveNumber]
- set analysis(lockSide$n) [sc_pos side]
+ set analysis(lockN$n) [sc_pos moveNumber]
+ set analysis(lockSide$n) [sc_pos side]
} else {
- # when i unlock the engine, i must restart the analysis if the engine is running
- # (it's possible to be here with the engine stopped, if i press the stop button
- # with the engine locked)
- if {$analysis(analyzeMode$n)} {
- stopAnalyzeMode $n
- startAnalyzeMode $n
- }
set state normal
}
set w ".analysisWin$n"
@@ -2678,6 +2659,7 @@ proc toggleLockEngine {n} {
$w.b1.annotate configure -state $state
$w.b1.bFinishGame configure -state $state
}
+ updateAnalysis $n
}
################################################################################
# updateAnalysisText
@@ -2975,66 +2957,87 @@ proc updateAnalysisBoard {n moves} {
# Restore pre-move command:
sc_info preMoveCmd preMoveCommand
}
+
+################################################################################
+# sendFENtoEngineUCI
+# Wait for the engine to be ready then send position and go infinite
+# engine_n: number of the engine that will receive the commands
+# delay: delay the commands - INTERNAL - DON'T USE OUTSIDE sendFENtoEngineUCI
+################################################################################
+proc sendFENtoEngineUCI {engine_n {delay 0}} {
+ global analysis
+ set analysis(after$engine_n) ""
+
+ if {$analysis(waitForReadyOk$engine_n) } {
+ #If too slow something is wrong: give up
+ if {$delay > 250} { return }
+
+ # Engine is not ready: process events, idle tasks and then call me back
+ incr delay
+ set cmd "set ::analysis(after$engine_n) "
+ append cmd { [ } " after $delay sendFENtoEngineUCI $engine_n $delay " { ] }
+ set analysis(after$engine_n) [eval [list after idle $cmd]]
+ } else {
+ sendToEngine $engine_n "position fen $analysis(fen$engine_n)"
+ sendToEngine $engine_n "go infinite"
+ }
+}
+
################################################################################
# updateAnalysis
# Update an analysis window by sending the current board
# to the engine.
################################################################################
proc updateAnalysis {{n 1}} {
- global analysisWin analysis windowsOS
+ global analysis
if {$analysis(pipe$n) == ""} { return }
-
# Just return if no output has been seen from the analysis program yet:
if {! $analysis(seen$n)} { return }
-
# No need to update if no analysis is running
if { ! $analysis(analyzeMode$n) } { return }
- # If too close to the previous update, and no other future update is
- # pending, reschedule this update to occur in another 0.3 seconds:
- #
- if {[catch {set clicks [clock clicks -milliseconds]}]} {
- set clicks [clock clicks]
- }
- set diff [expr {$clicks - $analysis(lastClicks$n)} ]
- if {$diff < 300 && $diff >= 0} {
- if {$analysis(after$n) == ""} {
- set analysis(after$n) [after 300 updateAnalysis $n]
- }
- return
- }
- set analysis(lastClicks$n) $clicks
- set analysis(after$n) ""
- after cancel updateAnalysis $n
-
- set old_movelist $analysis(movelist$n)
- set movelist [sc_game moves coord list]
- set analysis(movelist$n) $movelist
- set nonStdStart [sc_game startBoard]
- set old_nonStdStart $analysis(nonStdStart$n)
- set analysis(nonStdStart$n) $nonStdStart
-
# No need to send current board if engine is locked
if { $analysis(lockEngine$n) } { return }
-
+
if { $analysis(uci$n) } {
- # Normally the best move condition should still stand
- # Meaning that we will stop the engine here and wait for
- # the bestmove reply (which is handled in uci.tcl)
- if { $analysis(waitForBestMove$n) } {
- sendToEngine $n "stop"
- vwait analysis(waitForBestMove$n)
- }
- sendToEngine $n "position fen [sc_pos fen]"
- # Although we go infinite, some engines do a bestmove announcement
- # and go idle when they see a forced mate, before we stop them
- # Let's anticipate this.
- if { [expr { [string index [sc_game info previousMoveNT] end] != "#"}] } {
- set analysis(waitForBestMove$n) 1
- }
- sendToEngine $n "go infinite"
+ if {$analysis(after$n) == "" } {
+ if { $analysis(fen$n) != "" } { sendToEngine $n "stop" }
+ set analysis(waitForReadyOk$n) 1
+ sendToEngine $n "isready"
+ set analysis(after$n) [after idle "sendFENtoEngineUCI $n"]
+ }
set analysis(fen$n) [sc_pos fen]
set analysis(maxmovenumber$n) 0
+ set analysis(movelist$n) [sc_game moves coord list]
+ set analysis(nonStdStart$n) [sc_game startBoard]
} else {
+ #TODO: remove 0.3s delay even for other engines
+
+ global analysisWin windowsOS
+
+ # If too close to the previous update, and no other future update is
+ # pending, reschedule this update to occur in another 0.3 seconds:
+ #
+ if {[catch {set clicks [clock clicks -milliseconds]}]} {
+ set clicks [clock clicks]
+ }
+ set diff [expr {$clicks - $analysis(lastClicks$n)} ]
+ if {$diff < 300 && $diff >= 0} {
+ if {$analysis(after$n) == ""} {
+ set analysis(after$n) [after 300 updateAnalysis $n]
+ }
+ return
+ }
+ set analysis(lastClicks$n) $clicks
+ set analysis(after$n) ""
+ after cancel updateAnalysis $n
+
+ set old_movelist $analysis(movelist$n)
+ set movelist [sc_game moves coord list]
+ set analysis(movelist$n) $movelist
+ set nonStdStart [sc_game startBoard]
+ set old_nonStdStart $analysis(nonStdStart$n)
+ set analysis(nonStdStart$n) $nonStdStart
+
# This section is for engines that support "analyze":
if {$analysis(has_analyze$n)} {
sendToEngine $n "exit" ;# Get out of analyze mode, to send moves.
diff --git a/tcl/tools/uci.tcl b/tcl/tools/uci.tcl
index 42082a6..c32ae65 100644
--- a/tcl/tools/uci.tcl
+++ b/tcl/tools/uci.tcl
@@ -93,10 +93,7 @@ namespace eval uci {
# if {[string first "info currmove" $line ] == 0} { return }
logEngine $n "Engine: $line"
-
- # keep UI responsive when engine outputs lots of info (garbage ?)
- update idletasks
-
+
if {[string match "bestmove*" $line]} {
set data [split $line]
set uciInfo(bestmove$n) [lindex $data 1]
@@ -106,10 +103,8 @@ namespace eval uci {
} else {
set uciInfo(ponder$n) ""
}
- if { $analysis(waitForBestMove$n) } {
- set analysis(waitForBestMove$n) 0
- return
- }
+ set analysis(waitForBestMove$n) 0
+ return
}
if {[string match "id *name *" $line]} {
diff --git a/tcl/windows/tree.tcl b/tcl/windows/tree.tcl
index 20cb248..a7f8516 100644
--- a/tcl/windows/tree.tcl
+++ b/tcl/windows/tree.tcl
@@ -56,7 +56,7 @@ proc ::tree::treeFileSave {base} {
unbusyCursor .
}
################################################################################
-proc ::tree::make { { baseNumber -1 } } {
+proc ::tree::make { { baseNumber -1 } {locked 0} } {
global tree treeWin highcolor geometry helpMessage
if {$baseNumber == -1} {set baseNumber [sc_base current]}
@@ -77,7 +77,7 @@ proc ::tree::make { { baseNumber -1 } } {
set ::treeWin$baseNumber 1
set tree(training$baseNumber) 0
set tree(autorefresh$baseNumber) 1
- set tree(locked$baseNumber) 0
+ set tree(locked$baseNumber) $locked
set tree(base$baseNumber) $baseNumber
set tree(status$baseNumber) ""
set tree(bestMax$baseNumber) 50
@@ -428,20 +428,11 @@ proc ::tree::select { move baseNumber } {
set tree(refresh) 0
################################################################################
-proc ::tree::refresh { { baseNumber "" }} {
- set stack [lsearch -glob -inline -all [ wm stackorder . ] ".treeWin*"]
-
+proc ::tree::refresh { { baseNumber "" }} {
if {$baseNumber == "" } {
- sc_tree search -cancel all
-
- set topwindow [lindex [lsearch -glob -inline -all [ wm stackorder . ] ".treeWin*"] end ]
- set topbase -1
- if { [ catch { scan $topwindow ".treeWin%d" topbase } ] } {
- } else {
- ::tree::dorefresh $topbase
- }
- for {set i 1 } {$i <= [sc_base count total]} {incr i} {
- if { $i == $topbase } { continue }
+ sc_tree search -cancel all
+
+ for {set i [sc_base count total] } {$i > 0} {incr i -1} {
if { [::tree::dorefresh $i] == "canceled" } { break }
}
} else {
@@ -451,15 +442,12 @@ proc ::tree::refresh { { baseNumber "" }} {
################################################################################
proc ::tree::dorefresh { baseNumber } {
-
global tree treeWin glstart
set w .treeWin$baseNumber
if {![winfo exists $w]} { return }
-
if { ! $tree(autorefresh$baseNumber) } { return }
- busyCursor .
sc_progressBar $w.progress bar 251 16
foreach button {best graph training lock close} {
$w.buttons.$button configure -state disabled
@@ -483,7 +471,6 @@ proc ::tree::dorefresh { baseNumber } {
}
$w.buttons.stop configure -state disabled
- unbusyCursor .
set tree(refresh) 0
$w.f.tl configure -cursor {}
------------------------------------------------------------------------------
The ultimate all-in-one performance toolkit: Intel(R) Parallel Studio XE:
Pinpoint memory and threading errors before they happen.
Find and fix more than 250 security defects in the development cycle.
Locate bottlenecks in serial and parallel code that limit performance.
http://p.sf.net/sfu/intel-dev2devfeb
_______________________________________________
Scid-users mailing list
Scid-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/scid-users