]> code.delx.au - gnu-emacs-elpa/blobdiff - chess-display.el
Correctly indent `chess-with-current-buffer' in lisp-mode.
[gnu-emacs-elpa] / chess-display.el
index 15828171b1d786d588331afc1ed4cbdcfdda62c8..553de49b72e5f43bb7e8d950253c8bd5627350f3 100644 (file)
@@ -2,42 +2,65 @@
 ;;
 ;; Code shared by all chess displays
 ;;
-;; $Revision$
 
-;;; Code:
-
-(require 'chess-game)
+(require 'chess-message)
+(require 'chess-module)
 (require 'chess-var)
-(require 'chess-algebraic)
-(require 'chess-fen)
+(require 'chess-input)
+(require 'chess-random)
 
 (defgroup chess-display nil
   "Common code used by chess displays."
   :group 'chess)
 
-(defcustom chess-display-use-faces t
-  "If non-nil, provide colored faces for ASCII displays."
+(defcustom chess-display-popup t
+  "If non-nil (the default), popup displays whenever a significant event
+occurs."
   :type 'boolean
   :group 'chess-display)
 
-(defface chess-display-black-face
-  '((((class color) (background light)) (:foreground "Green"))
-    (((class color) (background dark)) (:foreground "Green"))
-    (t (:bold t)))
-  "*The face used for black pieces on the ASCII display."
+(make-variable-buffer-local 'chess-display-popup)
+
+(defcustom chess-display-highlight-legal nil
+  "If non-nil, highlight legal target squares when a piece is selected."
+  :type 'boolean
   :group 'chess-display)
 
-(defface chess-display-white-face
-  '((((class color) (background light)) (:foreground "Yellow"))
-    (((class color) (background dark)) (:foreground "Yellow"))
-    (t (:bold t)))
-  "*The face used for white pieces on the ASCII display."
+(chess-message-catalog 'english
+  '((mode-white     . "White")
+    (mode-black     . "Black")
+    (mode-start     . "START")
+    (mode-checkmate . "CHECKMATE")
+    (mode-aborted   . "ABORTED")
+    (mode-resigned  . "RESIGNED")
+    (mode-stalemate . "STALEMATE")
+    (mode-flag-fell . "FLAG FELL")
+    (mode-drawn     . "DRAWN")
+    (mode-edit      . "EDIT")))
+
+(defcustom chess-display-mode-line-format
+  '("  " chess-display-side-to-move "  "
+    chess-display-move-text "   "
+    (:eval (chess-display-clock-string))
+    "(" (:eval (chess-game-tag chess-module-game "White")) "-"
+    (:eval (chess-game-tag chess-module-game "Black")) ", "
+    (:eval (chess-game-tag chess-module-game "Site"))
+    (:eval (let ((date (chess-game-tag chess-module-game "Date")))
+            (and (string-match "\\`\\([0-9]\\{4\\}\\)" date)
+                 (concat " " (match-string 1 date))))) ")")
+  "The format of a chess display's modeline.
+See `mode-line-format' for syntax details."
+  :type 'sexp
+  :group 'chess-display)
+
+(defface chess-display-black-face
+  '((t (:background "Black" :foreground "White")))
+  "*The face used for the word Black in the mode-line."
   :group 'chess-display)
 
-(defface chess-display-highlight-face
-  '((((class color) (background light)) (:background "#add8e6"))
-    (((class color) (background dark)) (:background "#add8e6")))
-  "Face to use for highlighting pieces that have been selected."
+(defface chess-display-white-face
+  '((t (:background "White" :foreground "Black")))
+  "*The face used for the word White in the mode-line."
   :group 'chess-display)
 
 ;;; Code:
 ;; User interface
 ;;
 
-(defvar chess-display-style)
-(defvar chess-display-game)
-(defvar chess-display-variation)
 (defvar chess-display-index)
-(defvar chess-display-ply)
-(defvar chess-display-position)
+(defvar chess-display-move-text)
+(defvar chess-display-side-to-move)
 (defvar chess-display-perspective)
-(defvar chess-display-main-p nil)
 (defvar chess-display-event-handler nil)
-(defvar chess-display-no-popup nil)
 (defvar chess-display-edit-mode nil)
-(defvar chess-display-mode-line "")
+(defvar chess-display-index-positions nil)
 
-(make-variable-buffer-local 'chess-display-style)
-(make-variable-buffer-local 'chess-display-game)
-(make-variable-buffer-local 'chess-display-variation)
 (make-variable-buffer-local 'chess-display-index)
-(make-variable-buffer-local 'chess-display-ply)
-(make-variable-buffer-local 'chess-display-position)
+(make-variable-buffer-local 'chess-display-move-text)
+(make-variable-buffer-local 'chess-display-side-to-move)
+(put 'chess-display-side-to-move 'risky-local-variable t)
 (make-variable-buffer-local 'chess-display-perspective)
-(make-variable-buffer-local 'chess-display-main-p)
 (make-variable-buffer-local 'chess-display-event-handler)
-(make-variable-buffer-local 'chess-display-no-popup)
 (make-variable-buffer-local 'chess-display-edit-mode)
-(make-variable-buffer-local 'chess-display-mode-line)
-
-(defmacro chess-with-current-buffer (buffer &rest body)
-  `(let ((buf ,buffer))
-     (if buf
-        (with-current-buffer buf
-          ,@body)
-       ,@body)))
-
-(defun chess-display-create (style perspective)
-  "Create a chess display, for displaying chess objects."
-  (let* ((name (symbol-name style))
-        (handler (intern-soft (concat name "-handler"))))
-    (unless handler
-      (error "There is no such chessboard display style '%s'" name))
-    (with-current-buffer (generate-new-buffer "*Chessboard*")
-      (chess-display-mode)
-      (funcall handler 'initialize)
-      (setq chess-display-style style
-           chess-display-perspective perspective
-           chess-display-event-handler handler)
-      (add-hook 'kill-buffer-hook 'chess-display-quit nil t)
-      (current-buffer))))
+(make-variable-buffer-local 'chess-display-index-positions)
 
-(defun chess-display-clone (display style perspective)
-  (let ((new-display (chess-display-create style perspective)))
-    (with-current-buffer display
-      (cond
-       (chess-display-game
-       (chess-display-set-game new-display chess-display-game)
-       (chess-display-set-index new-display chess-display-index))
-       (chess-display-variation
-       (chess-display-set-variation new-display chess-display-variation)
-       (chess-display-set-index new-display chess-display-index))
-       (chess-display-ply
-       (chess-display-set-ply new-display chess-display-ply))
-       (chess-display-position
-       (chess-display-set-game new-display chess-display-position))))
-    (chess-display-update new-display t)
-    new-display))
+(defvar chess-display-handling-event nil
+  "If non-nil, chess-display is already handling the event.  This variable
+is used to avoid reentrancy.")
 
-(defun chess-display-destroy (display)
-  "Destroy a chess display object, killing all of its buffers."
-  (let ((buf (or display (current-buffer))))
-    (when (buffer-live-p buf)
-      (chess-display-event-handler (chess-display-game nil)
-                                  buf 'destroy)
-      (kill-buffer buf))))
+(defvar chess-display-style)
 
-(defsubst chess-display-style (display)
-  (chess-with-current-buffer display
-    chess-display-style))
+(chess-message-catalog 'english
+  '((no-such-style . "There is no such chessboard display style '%s'")
+    (cannot-yet-add . "Cannot insert moves into a game (yet)")))
+
+(defun chess-display-create (game style perspective)
+  "Create a chess display, for displaying chess objects.
+Where GAME is the chess game object to use, STYLE should be the display
+type to use (a symbol) and PERSPECTIVE determines the viewpoint
+of the board, if non-nil, the board is viewed from White's perspective."
+  (interactive (list (if current-prefix-arg
+                        (chess-game-create (chess-fen-to-pos
+                                            (read-string "FEN: ")))
+                      (chess-game-create))
+                     (intern-soft
+                      (concat "chess-" (completing-read "Display style: "
+                                                       '(("ics1")
+                                                         ("images")
+                                                         ("plain")))))
+                     (y-or-n-p "View from White's perspective? ")))
+  (if (require style nil t)
+      (let* ((chess-display-style style)
+            (display (chess-module-create 'chess-display game "*Chessboard*"
+                            perspective)))
+       (if (interactive-p)
+           (progn
+             (chess-display-update display)
+             (chess-display-popup display))
+         display))))
+
+(defalias 'chess-display-destroy 'chess-module-destroy)
+
+(defun chess-display-clone (display style perspective)
+  (let ((new-display (chess-display-create (chess-display-game display)
+                                          style perspective)))
+    ;; the display will have already been updated by the `set-' calls,
+    ;; it's just not visible yet
+    (chess-display-popup new-display)
+    new-display))
 
 (defsubst chess-display-perspective (display)
+  "Return the current perspective of DISPLAY."
   (chess-with-current-buffer display
     chess-display-perspective))
 
-(defun chess-display-set-perspective (display perspective)
-  (chess-with-current-buffer display
-    (setq chess-display-perspective perspective)
-    (erase-buffer)                     ; force a complete redraw
-    (chess-display-update nil)))
-
-(defsubst chess-display-main-p (display)
+(defun chess-display-set-perspective* (display perspective)
   (chess-with-current-buffer display
-    chess-display-main-p))
+    (setq chess-display-perspective perspective
+         chess-display-index-positions nil)
+    (erase-buffer)))                   ; force a complete redraw
 
-(defun chess-display-set-main (display)
-  (chess-with-current-buffer display
-    (setq chess-display-main-p t)))
-
-(defun chess-display-clear-main (display)
+(defun chess-display-set-perspective (display perspective)
+  "Set PERSPECTIVE of DISPLAY."
   (chess-with-current-buffer display
-    (setq chess-display-main-p nil)))
-
+    (chess-display-set-perspective* nil perspective)
+    (chess-display-update nil)))
 
-(defun chess-display-set-position (display position &optional search-func)
-  "Set the display position.
-Note that when a single position is being displayed, out of context of
-a game, the user's move will cause a new variation to be created,
-without a game object.
-If the position is merely edited, it will change the POSITION object
-that was passed in."
+(defun chess-display-set-position (display &optional position my-color)
+  "Set the game associated with DISPLAY to use POSITION and MY-COLOR."
   (chess-with-current-buffer display
-    (if chess-display-game
-       (chess-display-detach-game nil))
-    (setq chess-display-game nil
-         chess-display-variation nil
-         chess-display-index nil
-         chess-display-ply nil
-         chess-display-position position)
-    (chess-display-update nil t)))
+    (if position
+       (progn
+         (chess-game-set-start-position chess-module-game position)
+         (chess-game-set-data chess-module-game 'my-color my-color))
+      (chess-game-set-start-position chess-module-game
+                                    chess-starting-position)
+      (chess-game-set-data chess-module-game 'my-color t))
+    (chess-display-set-index nil 0)))
 
 (defun chess-display-position (display)
-  "Return the position currently viewed."
+  "Return the position currently viewed on DISPLAY."
   (chess-with-current-buffer display
-    (or (and chess-display-game
-            (chess-game-pos chess-display-game chess-display-index))
-       (and chess-display-variation
-            (chess-var-pos chess-display-variation chess-display-index))
-       (and chess-display-ply
-            (chess-ply-next-pos chess-display-ply))
-       chess-display-position)))
+    (if chess-display-edit-mode
+       chess-display-edit-position
+      (chess-game-pos chess-module-game chess-display-index))))
 
 (defun chess-display-set-ply (display ply)
-  "Set the display ply.
-This differs from a position display, only in that the algebraic form
-of the move made to the reach the displayed position will be shown in
-the modeline."
   (chess-with-current-buffer display
-    (if chess-display-game
-       (chess-display-detach-game nil))
-    (setq chess-display-game nil
-         chess-display-variation nil
-         chess-display-index nil
-         chess-display-ply ply
-         chess-display-position nil)
-    (chess-display-update display t)))
+    (let ((chess-game-inhibit-events t))
+      (chess-display-set-index nil 1))
+    (chess-game-set-plies chess-module-game
+                         (list ply (chess-ply-create*
+                                    (chess-ply-next-pos ply))))))
 
 (defun chess-display-ply (display)
   (chess-with-current-buffer display
-    (or (and chess-display-game
-            (chess-game-ply chess-display-game chess-display-index))
-       (and chess-display-variation
-            (chess-var-ply chess-display-variation chess-display-index))
-       chess-display-ply)))
+    (chess-game-ply chess-module-game chess-display-index)))
 
 (defun chess-display-set-variation (display variation &optional index)
-  "Set the display variation.
-This will cause the first ply in the variation to be displayed, with
-the user able to scroll back and forth through the moves in the
-variation.  Any moves made on the board will extend/change the
+  "Set DISPLAY VARIATION.
+If INDEX is not specified, this will cause the first ply in the variation
+to be displayed, with the user able to scroll back and forth through the
+moves in the variation.  Any moves made on the board will extend/change the
 variation that was passed in."
   (chess-with-current-buffer display
-    (if chess-display-game
-       (chess-display-detach-game nil))
-    (setq chess-display-game nil
-         chess-display-variation variation
-         chess-display-index (or index 0)
-         chess-display-ply nil
-         chess-display-position nil)
-    (chess-display-update nil t)))
+    (let ((chess-game-inhibit-events t))
+      (chess-display-set-index nil (or index (chess-var-index variation))))
+    (chess-game-set-plies chess-module-game variation)))
 
 (defun chess-display-variation (display)
   (chess-with-current-buffer display
-    (or (and chess-display-game
-            (chess-game-main-var chess-display-game))
-       chess-display-variation)))
+    (chess-game-main-var chess-module-game)))
 
-(defun chess-display-set-game (display game &optional index)
-  "Set the display game.
-This will cause the first ply in the game's main variation to be
-displayed.  Also, information about the game is shown in the
-modeline."
-  (chess-with-current-buffer display
-    (if chess-display-game
-       (chess-display-detach-game nil))
-    (setq chess-display-game game
-         chess-display-variation nil
-         chess-display-index (or index 0)
-         chess-display-ply nil
-         chess-display-position nil)
-    (chess-game-add-hook game 'chess-display-event-handler display)
-    (chess-display-update nil t)))
-
-(defun chess-display-detach-game (display)
-  "Set the display game.
-This will cause the first ply in the game's main variation to be
-displayed.  Also, information about the game is shown in the
-modeline."
+(defun chess-display-set-game* (display game &optional index)
+  "Set the game associated with the given DISPLAY."
   (chess-with-current-buffer display
-    (if chess-display-game
-       (chess-game-remove-hook chess-display-game
-                               'chess-display-event-handler
-                               (or display (current-buffer))))))
+    (chess-module-set-game* display game)
+    (chess-display-set-index nil (or index (chess-game-index game)))))
 
-(defsubst chess-display-game (display)
+(defun chess-display-set-game (display game &optional index)
+  "Set the given DISPLAY to display the GAME object, optionally at INDEX.
+This is the function to call to cause a display to view a game.  It
+will also update all of the listening engines and other displays to
+also view the same game."
   (chess-with-current-buffer display
-    chess-display-game))
+    (chess-game-copy-game chess-module-game game)
+    (chess-display-set-index nil (or index (chess-game-index game)))))
+
+(defalias 'chess-display-game 'chess-module-game)
+
+(defun chess-display-clock-string ()
+  (let ((white (chess-game-data chess-module-game 'white-remaining))
+       (black (chess-game-data chess-module-game 'black-remaining)))
+    (unless (and white black)
+      (let ((last-ply (chess-game-ply chess-module-game
+                                     (1- chess-display-index))))
+       (setq white (chess-ply-keyword last-ply :white)
+             black (chess-ply-keyword last-ply :black))))
+    (if (and white black)
+       (format "W %s%02d:%02d B %s%02d:%02d   "
+               (if (and (< white 0) (= 0 (floor white))) "-" "")
+               (/ (floor white) 60) (% (abs (floor white)) 60)
+               (if (and (< black 0) (= 0 (floor black))) "-" "")
+               (/ (floor black) 60) (% (abs (floor black)) 60)))))
 
 (defun chess-display-set-index (display index)
   (chess-with-current-buffer display
-    (unless chess-display-index
-      (error "There is no game or variation currently being displayed."))
-    (unless (or (not (integerp index))
-               (< index 0)
-               (> index (if chess-display-game
-                            (chess-game-index chess-display-game)
-                          (chess-var-index chess-display-variation))))
-      (setq chess-display-index index)
-      (chess-display-update nil))))
+    (if (not (or (not (integerp index))
+                (< index 0)
+                (> index (chess-game-index chess-module-game))))
+       (chess-game-run-hooks chess-module-game 'set-index index)
+      (when (and (> index (chess-game-index chess-module-game))
+                (not (chess-ply-final-p (chess-game-ply chess-module-game))))
+       (chess-game-run-hooks chess-module-game 'forward)))))
+
+(defun chess-display-set-index* (display index)
+  (chess-with-current-buffer display
+    (setq chess-display-index index
+         chess-display-move-text
+         (if (= index 0)
+             (chess-string 'mode-start)
+           (concat (int-to-string (if (> index 1)
+                                      (if (= (mod index 2) 0)
+                                          (/ index 2)
+                                        (1+ (/ index 2)))
+                                    1))
+                   "." (and (= 0 (mod index 2)) "..")
+                   (chess-ply-to-algebraic
+                    (chess-game-ply chess-module-game (1- index)))))
+         chess-display-side-to-move
+         (let ((status (chess-game-status chess-module-game index)))
+           (cond
+            ((eq status :aborted)   (chess-string 'mode-aborted))
+            ((eq status :resign)    (chess-string 'mode-resigned))
+            ((eq status :drawn)     (chess-string 'mode-drawn))
+            ((eq status :checkmate) (chess-string 'mode-checkmate))
+            ((eq status :stalemate) (chess-string 'mode-stalemate))
+            ((eq status :flag-fell) (chess-string 'mode-flag-fell))
+            (t
+             (let* ((color (or chess-pos-always-white
+                               (chess-game-side-to-move chess-module-game
+                                                        index)))
+                    (str (format " %s " (if color
+                                            (chess-string 'mode-white)
+                                          (chess-string 'mode-black)))))
+               (add-text-properties 0 (length str)
+                                    (list 'face (if color
+                                                    'chess-display-white-face
+                                                  'chess-display-black-face))
+                                    str)
+               str)))))
+    (force-mode-line-update)))
 
 (defsubst chess-display-index (display)
   (chess-with-current-buffer display
@@ -271,33 +283,96 @@ modeline."
   "Update the chessboard DISPLAY.  POPUP too, if that arg is non-nil."
   (chess-with-current-buffer display
     (funcall chess-display-event-handler 'draw
-            (chess-display-position nil)
-            (chess-display-perspective nil))
-    (chess-display-set-modeline)
-    (if popup
+            (chess-display-position nil) chess-display-perspective)
+    (if (and popup chess-display-popup
+            (chess-module-leader-p nil))
        (chess-display-popup nil))))
 
-(defun chess-display-move (display ply)
+(defun chess-display-redraw (&optional display)
+  "Just redraw the current display."
+  (interactive)
+  (chess-with-current-buffer display
+    (let ((here (point)))
+      (erase-buffer)
+      (chess-display-update nil)
+      (goto-char here))))
+
+(defun chess-display-index-pos (display index)
+  (chess-with-current-buffer display
+    (unless chess-display-index-positions
+      (setq chess-display-index-positions (make-vector 64 nil))
+      (let ((pos (next-single-property-change (point-min) 'chess-coord))
+           pos-index)
+       (while pos
+         (if (setq pos-index (get-text-property pos 'chess-coord))
+             (aset chess-display-index-positions pos-index pos))
+         (setq pos (next-single-property-change pos 'chess-coord)))
+       (unless (aref chess-display-index-positions 0)
+         (aset chess-display-index-positions 0
+               (if chess-display-perspective
+                   (point-min)
+                 (1- (point-max)))))
+       (unless (aref chess-display-index-positions 63)
+         (aset chess-display-index-positions 63
+               (if chess-display-perspective
+                   (1- (point-max))
+                 (point-min))))))
+    (aref chess-display-index-positions index)))
+
+(defun chess-display-paint-move (display ply)
+  (chess-with-current-buffer display
+    (let ((position (chess-ply-pos ply))
+         (ch (chess-ply-changes ply)))
+      (while ch
+       (if (symbolp (car ch))
+           (setq ch nil)
+         (let ((from (car ch))
+               (to (cadr ch)))
+           (funcall chess-display-event-handler 'draw-square
+                    (chess-display-index-pos nil from) ?  from)
+           (let ((new-piece (chess-ply-keyword ply :promote)))
+             (if new-piece
+                 (funcall chess-display-event-handler 'draw-square
+                          (chess-display-index-pos nil to)
+                          (if (chess-pos-side-to-move position)
+                              new-piece
+                            (downcase new-piece)) to)
+               (funcall chess-display-event-handler 'draw-square
+                        (chess-display-index-pos nil to)
+                        (chess-pos-piece position from) to)))
+           (when (chess-ply-keyword ply :en-passant)
+             (funcall chess-display-event-handler 'draw-square
+                      (chess-display-index-pos nil (chess-pos-en-passant position))
+                      ?  (chess-pos-en-passant position))))
+         (setq ch (cddr ch)))))))
+
+(chess-message-catalog 'english
+  '((not-your-move . "It is not your turn to move")
+    (game-is-over  . "This game is over")))
+
+(defun chess-display-move (display ply &optional prev-pos pos)
   "Move a piece on DISPLAY, by applying the given PLY.
 The position of PLY must match the currently displayed position.
 If only START is given, it must be in algebraic move notation."
   (chess-with-current-buffer display
-    (cond
-     (chess-display-game
+    (if (and (chess-display-active-p)
+            ;; `active' means we're playing against an engine
+            (chess-game-data chess-module-game 'active)
+            (not (eq (chess-game-data chess-module-game 'my-color)
+                     (chess-game-side-to-move chess-module-game))))
+       (chess-error 'not-your-move)
+      (if (and (= chess-display-index
+                 (chess-game-index chess-module-game))
+              (chess-game-over-p chess-module-game))
+         (chess-error 'game-is-over)))
+    (if (= chess-display-index (chess-game-index chess-module-game))
+       (let ((chess-display-handling-event t))
+         (chess-game-move chess-module-game ply)
+         (chess-display-paint-move nil ply)
+         (chess-display-set-index* nil (chess-game-index chess-module-game)))
       ;; jww (2002-03-28): This should beget a variation within the
       ;; game, or alter the game, just as SCID allows
-      (if (= (chess-display-index nil)
-            (chess-game-index chess-display-game))
-         (chess-game-move chess-display-game ply)
-       (error "What to do here??  NYI")))
-     (chess-display-variation
-      (chess-var-move chess-display-variation ply)
-      (chess-display-set-index nil (chess-var-index chess-display-variation)))
-     (chess-display-ply
-      (setq chess-display-ply ply))
-     (chess-display-position           ; an ordinary position
-      (setq chess-display-position (chess-ply-next-pos ply))))
-    (chess-display-update nil)))
+      (chess-error 'cannot-yet-add))))
 
 (defun chess-display-highlight (display &rest args)
   "Highlight the square at INDEX on the current position.
@@ -307,72 +382,146 @@ that is supported by most displays, and is the default mode."
   (chess-with-current-buffer display
     (let ((mode :selected))
       (dolist (arg args)
-       (if (symbolp arg)
+       (if (or (symbolp arg) (stringp arg))
            (setq mode arg)
-         (funcall chess-display-event-handler
-                  'highlight arg mode))))))
+         (funcall chess-display-event-handler 'highlight arg mode))))))
+
+(defun chess-display-highlight-legal (display pos)
+  "Highlight all legal move targets from POS."
+  (chess-with-current-buffer display
+    (dolist (ply (chess-legal-plies (chess-display-position nil)
+                                   :index pos))
+      (chess-display-highlight nil "pale green"
+                              (chess-ply-target ply)))))
+
+(defun chess-display-highlight-passed-pawns (&optional display)
+  (interactive)
+  (mapc
+   (lambda (index) (chess-display-highlight display index :selected))
+   (append
+    (chess-pos-passed-pawns (chess-display-position display) t)
+    (chess-pos-passed-pawns (chess-display-position display) nil))))
 
 (defun chess-display-popup (display)
   "Popup the given DISPLAY, so that it's visible to the user."
   (chess-with-current-buffer display
-    (unless chess-display-no-popup
+    (unless (eq (get-buffer-window (current-buffer))
+               (selected-window))
       (funcall chess-display-event-handler 'popup))))
 
 (defun chess-display-enable-popup (display)
   "Popup the given DISPLAY, so that it's visible to the user."
   (chess-with-current-buffer display
-    (setq chess-display-no-popup nil)))
+    (setq chess-display-popup nil)))
 
 (defun chess-display-disable-popup (display)
   "Popup the given DISPLAY, so that it's visible to the user."
   (chess-with-current-buffer display
-    (setq chess-display-no-popup t)))
+    (setq chess-display-popup t)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
-;; Event handler
+;; Default window and frame popup functions
 ;;
 
-(defun chess-display-event-handler (game display event &rest args)
-  "This display module presents a standard chessboard.
-See `chess-display-type' for the different kinds of displays."
-  (with-current-buffer display
-    (cond
-     ((eq event 'shutdown)
-      (chess-display-destroy nil))
-
-     ((eq event 'destroy)
-      (chess-display-detach-game nil))
+(defun chess-display-popup-in-window ()
+  "Popup the given DISPLAY, so that it's visible to the user."
+  (unless (get-buffer-window (current-buffer))
+    (if (> (length (window-list)) 1)
+       (fit-window-to-buffer (display-buffer (current-buffer)))
+      (display-buffer (current-buffer)))))
 
-     ((eq event 'pass)
-      (let ((my-color (if chess-display-game
-                         (chess-game-get-data chess-display-game
-                                              'my-color)
-                       (chess-display-perspective nil))))
-       (if chess-display-game
-           (chess-game-set-data chess-display-game 'my-color
-                                (not my-color)))
-       (chess-display-set-perspective nil (not my-color))))
+(defun chess-display-popup-in-frame (height width font
+                                    &optional display no-minibuffer)
+  "Popup the given DISPLAY, so that it's visible to the user."
+  (let ((window (get-buffer-window (current-buffer) t)))
+    (if window
+       (let ((frame (window-frame window)))
+         (unless (eq frame (selected-frame))
+           (raise-frame frame)))
+      (let ((params (list (cons 'name "*Chessboard*")
+                         (cons 'height height)
+                         (cons 'width width))))
+       (if display
+           (push (cons 'display display) params))
+       (if font
+           (push (cons 'font font) params))
+       (if no-minibuffer
+           (push (cons 'minibuffer nil) params))
+       (select-frame (make-frame params))
+       (set-window-dedicated-p (selected-window) t)))))
 
-     ((memq event '(move game-over resign))
-      (chess-display-set-index nil (chess-game-index
-                                   (chess-display-game nil)))))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Event handler
+;;
 
-    (if (eq event 'resign)
-       (message-box "%s resigns" (if (car args) "White" "Black")))
+(defcustom chess-display-interesting-events
+  '(set-index)
+  "Events which will cause a display refresh."
+  :type '(repeat symbol)
+  :group 'chess-display)
 
-    (unless (eq event 'shutdown)
-      (chess-display-update nil))
+(defcustom chess-display-momentous-events
+  '(orient post-undo setup-game pass move resign abort)
+  "Events that will refresh, and cause 'main' displays to popup.
+These are displays for which `chess-display-set-main' has been
+called."
+  :type '(repeat symbol)
+  :group 'chess-display)
 
-    (if (memq event '(pass move game-over resign))
-       (chess-display-popup nil))))
+(defun chess-display-handler (game event &rest args)
+  "This display module presents a standard chessboard.
+See `chess-display-type' for the different kinds of displays."
+  (unless chess-display-handling-event
+    (if (eq event 'initialize)
+       (progn
+         (chess-display-mode)
+         (setq chess-display-index (chess-game-index game)
+               chess-display-side-to-move
+               (if (chess-pos-side-to-move (chess-game-pos game))
+                   (chess-string 'mode-white)
+                 (chess-string 'mode-black))
+               chess-display-move-text (chess-string 'mode-start)
+               chess-display-perspective (car args)
+               chess-display-event-handler
+               (intern-soft (concat (symbol-name chess-display-style)
+                                    "-handler")))
+         (and chess-display-event-handler
+              (funcall chess-display-event-handler 'initialize)))
+      (cond
+       ((eq event 'pass)
+       (let ((my-color (chess-game-data game 'my-color)))
+         (chess-game-set-data game 'my-color (not my-color))
+         (chess-display-set-perspective* nil (not my-color))))
+
+       ((eq event 'set-index)
+       (chess-display-set-index* nil (car args)))
+
+       ((eq event 'orient)
+       (let ((my-color (chess-game-data game 'my-color)))
+         ;; Set the display's perspective to whichever color I'm
+         ;; playing
+         (chess-display-set-perspective* nil my-color))))
+
+      (if (memq event chess-display-momentous-events)
+         (progn
+           (chess-display-set-index* nil (chess-game-index game))
+           (if (eq event 'move)
+               (progn
+                 (chess-display-paint-move nil (car args))
+                 (if chess-display-popup
+                     (chess-display-popup nil)))
+             (chess-display-update nil chess-display-popup)))
+       (if (memq event chess-display-interesting-events)
+           (chess-display-update nil))))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
 ;; chess-display-mode
 ;;
 
-(defvar chess-display-mode-map
+(defvar chess-display-safe-map
   (let ((map (make-keymap)))
     (suppress-keymap map)
     (set-keymap-parent map nil)
@@ -381,17 +530,9 @@ See `chess-display-type' for the different kinds of displays."
     (define-key map [tab] 'chess-display-invert)
 
     (define-key map [??] 'describe-mode)
-    (define-key map [?B] 'chess-display-list-buffers)
-    (define-key map [?C] 'chess-display-duplicate)
-    (define-key map [?E] 'chess-display-edit-board)
-    (define-key map [?F] 'chess-display-set-from-fen)
+    (define-key map [?L] 'chess-display-list-buffers)
+    ;;(define-key map [?C] 'chess-display-duplicate)
     (define-key map [?I] 'chess-display-invert)
-    (define-key map [?X] 'chess-display-quit)
-    (define-key map [?M] 'chess-display-manual-move)
-    (define-key map [?@] 'chess-display-remote)
-    (define-key map [? ] 'chess-display-pass)
-    (define-key map [?S] 'chess-display-shuffle)
-    (define-key map [?R] 'chess-display-resign)
 
     (define-key map [?<] 'chess-display-move-first)
     (define-key map [?,] 'chess-display-move-backward)
@@ -400,27 +541,68 @@ See `chess-display-type' for the different kinds of displays."
     (define-key map [?.] 'chess-display-move-forward)
     (define-key map [(meta ?>)] 'chess-display-move-last)
 
-    (define-key map [(meta ?w)] 'chess-display-copy-board)
-    (define-key map [(control ?y)] 'chess-display-paste-board)
+    (define-key map [(meta ?w)] 'chess-display-kill-board)
 
     (define-key map [(control ?l)] 'chess-display-redraw)
 
+    map)
+  "The mode map used in read-only display buffers.")
+
+(defvar chess-display-mode-map
+  (let ((map (copy-keymap chess-display-safe-map)))
+    (define-key map [space] 'chess-display-pass)
+    (define-key map [? ] 'chess-display-pass)
+    (define-key map [??] 'describe-mode)
+    (define-key map [?@] 'chess-display-remote)
+    (define-key map [?A] 'chess-display-manual-move)
+    (define-key map [(control ?c) (control ?a)] 'chess-display-abort)
+    (define-key map [?C] 'chess-display-duplicate)
+    (define-key map [?D] 'chess-display-decline)
+    (define-key map [(control ?c) (control ?c)] 'chess-display-force)
+    (define-key map [(control ?c) (control ?d)] 'chess-display-draw)
+    (define-key map [?E] 'chess-display-edit-board)
+    (define-key map [?F] 'chess-display-set-from-fen)
+    (define-key map [(control ?c) (control ?f)] 'chess-display-call-flag)
+    (define-key map [?M] 'chess-display-match)
+    (define-key map [(control ?c) (control ?r)] 'chess-display-resign)
+    (define-key map [?R] 'chess-display-retract)
+    (define-key map [?S] 'chess-display-shuffle)
+    (define-key map [(control ?c) (control ?t)] 'chess-display-undo)
+    (define-key map [?X] 'chess-display-quit)
+    (define-key map [?Y] 'chess-display-accept)
+
+    (define-key map [?\{] 'chess-display-annotate)
+    (define-key map [?\"] 'chess-display-chat)
+    (define-key map [?\'] 'chess-display-chat)
+    (define-key map [?\~] 'chess-display-check-autosave)
+
+    (define-key map [(control ?r)] 'chess-display-search-backward)
+    (define-key map [(control ?s)] 'chess-display-search-forward)
+    (define-key map [(control ?y)] 'chess-display-yank-board)
+
     (dolist (key '(?a ?b ?c ?d ?e ?f ?g ?h
-                     ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8
-                     ?r ?n ?b ?q ?k ?o))
-      (define-key map (vector key) 'chess-keyboard-shortcut))
-    (define-key map [backspace] 'chess-keyboard-shortcut-delete)
-    (define-key map [?x] 'ignore)
+                  ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8
+                  ?r ?n ?b ?q ?k
+                  ?R ?N ?B ?Q ?K
+                  ?o ?O ?x))
+      (define-key map (vector key) 'chess-input-shortcut))
+    (define-key map [backspace] 'chess-input-shortcut-delete)
 
     (define-key map [(control ?m)] 'chess-display-select-piece)
     (define-key map [return] 'chess-display-select-piece)
     (cond
      ((featurep 'xemacs)
       (define-key map [(button1)] 'chess-display-mouse-select-piece)
-      (define-key map [(button2)] 'chess-display-mouse-select-piece))
+      (define-key map [(button2)] 'chess-display-mouse-select-piece)
+      (define-key map [(button3)] 'ignore))
      (t
-      (define-key map [mouse-1] 'chess-display-mouse-select-piece)
-      (define-key map [mouse-2] 'chess-display-mouse-select-piece)))
+      (define-key map [down-mouse-1] 'chess-display-mouse-select-piece)
+      (define-key map [drag-mouse-1] 'chess-display-mouse-select-piece)
+
+      (define-key map [down-mouse-2] 'chess-display-mouse-select-piece)
+      (define-key map [drag-mouse-2] 'chess-display-mouse-select-piece)
+
+      (define-key map [mouse-3] 'ignore)))
 
     (define-key map [menu-bar files] 'undefined)
     (define-key map [menu-bar edit] 'undefined)
@@ -442,104 +624,181 @@ See `chess-display-type' for the different kinds of displays."
       ["Next" chess-display-move-forward t]
       ["Last" chess-display-move-last t])))
 
-(defun chess-display-redraw ()
-  "Just redraw the current display."
-  (interactive)
-  (chess-display-update nil))
-
 (defun chess-display-mode ()
   "A mode for displaying and interacting with a chessboard.
 The key bindings available in this mode are:
 \\{chess-display-mode-map}"
   (interactive)
-  (setq major-mode 'chess-display-mode mode-name "Chessboard")
+  (setq major-mode 'chess-display-mode
+       mode-name "Chessboard")
   (use-local-map chess-display-mode-map)
   (buffer-disable-undo)
   (setq buffer-auto-save-file-name nil
-       mode-line-format 'chess-display-mode-line))
-
-(defun chess-display-set-modeline ()
-  "Set the modeline to reflect the current game position."
-  (let ((color (chess-pos-side-to-move (chess-display-position nil)))
-       (index (chess-display-index nil))
-       ply)
-    (if (null index)
-       (setq chess-display-mode-line
-             (if color "  White to move" "  Black to move"))
-      (if (and index (= index 0))
-         (setq chess-display-mode-line
-               (format "   %s   START" (if color "White" "Black")))
-       (cond
-        (chess-display-ply
-         (setq ply chess-display-ply))
-        (chess-display-game
-         (setq ply (chess-game-ply chess-display-game (1- index))))
-        (chess-display-variation
-         (setq ply (chess-var-ply chess-display-variation (1- index)))))
-       (if ply
-           (setq chess-display-mode-line
-                 (concat
-                  (if (chess-ply-final-p ply)
-                      "  FINISHED"
-                    (concat "  " (if color "White" "Black")))
-                  (if index
-                      (concat "   " (int-to-string
-                                     (if (> index 1)
-                                         (/ index 2) (1+ (/ index 2))))))
-                  (if ply
-                      (concat ". " (if color "... ")
-                              (or (chess-ply-to-algebraic ply)
-                                  "???"))))))))))
+       mode-line-format chess-display-mode-line-format)
+  (setq chess-input-position-function
+       (function
+        (lambda ()
+          (chess-display-position nil))))
+  (setq chess-input-move-function 'chess-display-move))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Commands used by the keyboard bindings above
+;;
 
 (defsubst chess-display-active-p ()
   "Return non-nil if the displayed chessboard reflects an active game.
 Basically, it means we are playing, not editing or reviewing."
-  (and chess-display-game
-       (= (chess-display-index nil)
-         (chess-game-index chess-display-game))
+  (and (chess-game-data chess-module-game 'active)
+       (= chess-display-index
+         (chess-game-index chess-module-game))
+       (not (chess-game-over-p chess-module-game))
        (not chess-display-edit-mode)))
 
 (defun chess-display-invert ()
   "Invert the perspective of the current chess board."
   (interactive)
-  (chess-display-set-perspective nil (not (chess-display-perspective nil))))
+  (chess-display-set-perspective nil (not chess-display-perspective)))
 
 (defun chess-display-set-from-fen (fen)
   "Send the current board configuration to the user."
   (interactive "sSet from FEN string: ")
   (chess-display-set-position nil (chess-fen-to-pos fen)))
 
-(defun chess-display-copy-board ()
+(defun chess-display-kill-board (&optional arg)
+  "Send the current board configuration to the user."
+  (interactive "P")
+  (let ((x-select-enable-clipboard t)
+       (game chess-module-game))
+    (if arg
+       (kill-new (with-temp-buffer
+                   (chess-game-to-pgn game)
+                   (buffer-string)))
+      (kill-new (chess-pos-to-fen (chess-display-position nil))))))
+
+(defun chess-display-yank-board ()
   "Send the current board configuration to the user."
   (interactive)
-  (let* ((x-select-enable-clipboard t)
-        (fen (chess-pos-to-fen (chess-display-position nil))))
-    (kill-new fen)))
+  (let ((x-select-enable-clipboard t)
+       (display (current-buffer))
+       (text (current-kill 0)))
+    (with-temp-buffer
+      (insert text)
+      (goto-char (point-max))
+      (while (and (bolp) (not (bobp)))
+       (delete-backward-char 1))
+      (goto-char (point-min))
+      (cond
+       ((search-forward "[Event " nil t)
+       (goto-char (match-beginning 0))
+       (chess-game-copy-game chess-module-game (chess-pgn-to-game)))
+       ((looking-at (concat chess-algebraic-regexp "$"))
+       (let ((move (buffer-string)))
+         (with-current-buffer display
+           (chess-display-manual-move move))))
+       (t
+       (with-current-buffer display
+         (chess-display-set-from-fen (buffer-string))))))))
 
-(defun chess-display-paste-board ()
-  "Send the current board configuration to the user."
+(defvar chess-display-search-map
+  (let ((map (copy-keymap minibuffer-local-map)))
+    (dolist (key '(?a ?b ?c ?d ?e ?f ?g ?h
+                     ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8
+                     ?r ?n ?b ?q ?k
+                     ?R ?N ?B ?Q ?K
+                     ?o ?O ?x))
+      (define-key map (vector key) 'chess-display-search-key))
+    (define-key map [backspace] 'chess-display-search-delete)
+    (define-key map [delete] 'chess-display-search-delete)
+    (define-key map [(control ?h)] 'chess-display-search-delete)
+    (define-key map [(control ?r)] 'chess-display-search-again)
+    (define-key map [(control ?s)] 'chess-display-search-again)
+    map))
+
+(defvar chess-display-search-direction nil)
+(defvar chess-current-display nil)
+(defvar chess-display-previous-index nil)
+
+(make-variable-buffer-local 'chess-display-previous-index)
+
+(chess-message-catalog 'english
+  '((san-not-found . "Could not find a matching move")))
+
+(defun chess-display-search (&optional reset again)
+  (interactive)
+  (let ((str (concat "\\`" (minibuffer-contents)))
+       limit index)
+    (with-current-buffer chess-current-display
+      (setq index (if reset
+                     chess-display-previous-index
+                   chess-display-index))
+      (if again
+         (setq index (if chess-display-search-direction
+                         (1+ index)
+                       (- index 2))))
+      (catch 'found
+       (while (if chess-display-search-direction
+                  (< index (or limit
+                               (setq limit
+                                     (chess-game-index chess-module-game))))
+                (>= index 0))
+         (let* ((ply (chess-game-ply chess-module-game index))
+                (san (chess-ply-keyword ply :san))
+                (case-fold-search t))
+           (when (and san (string-match str san))
+             (chess-display-set-index nil (1+ index))
+             (throw 'found t)))
+         (setq index (funcall (if chess-display-search-direction '1+ '1-)
+                              index)))
+       (chess-error 'san-not-found)))))
+
+(defun chess-display-search-again ()
   (interactive)
-  (let* ((x-select-enable-clipboard t)
-        (fen (current-kill 0)))
-    (chess-display-set-from-fen fen)))
+  (chess-display-search nil t))
 
-(defun chess-display-set-piece ()
-  "Set the piece under point to command character, or space for clear."
+(defun chess-display-search-key ()
   (interactive)
-  (unless (chess-display-active-p)
-    (chess-pos-set-piece (chess-display-position nil)
-                        (get-text-property (point) 'chess-coord)
-                        last-command-char)
-    (chess-display-update nil)))
+  (call-interactively 'self-insert-command)
+  (chess-display-search))
+
+(defun chess-display-search-delete ()
+  (interactive)
+  (call-interactively 'delete-backward-char)
+  (chess-display-search t))
+
+(defun chess-display-search-backward (&optional direction)
+  (interactive)
+  (setq chess-display-previous-index chess-display-index)
+  (condition-case err
+      (let ((chess-display-search-direction direction)
+           (chess-current-display (current-buffer)))
+       (read-from-minibuffer "Find algebraic move: " nil
+                             chess-display-search-map))
+    (quit
+     (chess-display-set-index nil chess-display-previous-index))))
+
+(defun chess-display-search-forward ()
+  (interactive)
+  (chess-display-search-backward t))
+
+(chess-message-catalog 'english
+  '((illegal-notation . "Illegal move notation: %s")
+    (want-to-quit     . "Do you really want to quit? ")))
 
 (defun chess-display-quit ()
-  "Quit the current game."
+  "Quit the game associated with the current display."
+  (interactive)
+  (if (or (not (chess-module-leader-p nil))
+         (yes-or-no-p (chess-string 'want-to-quit)))
+      (chess-module-destroy nil)))
+
+(defun chess-display-annotate ()
+  (interactive)
+  (chess-game-run-hooks chess-module-game 'switch-to-annotations))
+
+(defun chess-display-chat ()
   (interactive)
-  (remove-hook 'kill-buffer-hook 'chess-display-quit t)
-  (if (and chess-display-main-p
-          chess-display-game)
-      (chess-game-run-hooks chess-display-game 'shutdown)
-    (chess-display-destroy nil)))
+  (chess-game-run-hooks chess-module-game 'switch-to-chat))
 
 (defun chess-display-manual-move (move)
   "Move a piece manually, using chess notation."
@@ -548,10 +807,10 @@ Basically, it means we are playing, not editing or reviewing."
          (format "%s(%d): "
                  (if (chess-pos-side-to-move (chess-display-position nil))
                      "White" "Black")
-                 (1+ (/ (or (chess-display-index nil) 0) 2))))))
+                 (1+ (/ (or chess-display-index 0) 2))))))
   (let ((ply (chess-algebraic-to-ply (chess-display-position nil) move)))
     (unless ply
-      (error "Illegal move notation: %s" move))
+      (chess-error 'illegal-notation move))
     (chess-display-move nil ply)))
 
 (defun chess-display-remote (display)
@@ -559,21 +818,20 @@ Basically, it means we are playing, not editing or reviewing."
   (require 'chess-images)
   (let ((chess-images-separate-frame display))
     (chess-display-clone (current-buffer) 'chess-images
-                        (chess-display-perspective nil))))
+                        chess-display-perspective)))
 
 (defun chess-display-duplicate (style)
   (interactive
-   (list (read-from-minibuffer "Create new display using style: "
-                              (symbol-name (chess-display-style nil)))))
+   (list (concat "chess-"
+                (read-from-minibuffer "Create new display using style: "))))
   (chess-display-clone (current-buffer) (intern-soft style)
-                      (chess-display-perspective nil)))
+                      chess-display-perspective))
 
 (defun chess-display-pass ()
   "Pass the move to your opponent.  Only valid on the first move."
   (interactive)
-  (if (and (chess-display-active-p)
-          (= 0 (chess-display-index nil)))
-      (chess-game-run-hooks chess-display-game 'pass)
+  (if (chess-display-active-p)
+      (chess-game-run-hooks chess-module-game 'pass)
     (ding)))
 
 (defun chess-display-shuffle ()
@@ -581,39 +839,118 @@ Basically, it means we are playing, not editing or reviewing."
   (interactive)
   (require 'chess-random)
   (if (and (chess-display-active-p)
-          (= 0 (chess-display-index nil)))
-      (chess-game-set-start-position chess-display-game
+          (= 0 chess-display-index))
+      (chess-game-set-start-position chess-module-game
                                     (chess-fischer-random-position))
     (ding)))
 
+(defun chess-display-match ()
+  "Request a match with any listening engine."
+  (interactive)
+  (chess-game-run-hooks chess-module-game 'match))
+
+(defun chess-display-accept ()
+  (interactive)
+  (if (chess-display-active-p)
+      (chess-game-run-hooks chess-module-game 'accept)
+    (ding)))
+
+(defun chess-display-decline ()
+  (interactive)
+  (if (chess-display-active-p)
+      (chess-game-run-hooks chess-module-game 'decline)
+    (ding)))
+
+(defun chess-display-retract ()
+  (interactive)
+  (if (chess-display-active-p)
+      (chess-game-run-hooks chess-module-game 'retract)
+    (ding)))
+
+(defun chess-display-call-flag ()
+  (interactive)
+  (if (chess-display-active-p)
+      (chess-game-run-hooks chess-module-game 'call-flag)
+    (ding)))
+
+(defun chess-display-force ()
+  (interactive)
+  (if (chess-display-active-p)
+      (chess-game-run-hooks chess-module-game 'force)
+    (ding)))
+
+(defun chess-display-check-autosave ()
+  (interactive)
+  (if (chess-display-active-p)
+      (chess-game-run-hooks chess-module-game 'check-autosave)
+    (ding)))
+
 (defun chess-display-resign ()
-  "Generate a shuffled opening position."
+  "Resign the current game."
+  (interactive)
+  (if (chess-display-active-p)
+      (chess-game-end chess-module-game :resign)
+    (ding)))
+
+(defun chess-display-abort ()
+  "Abort the current game."
+  (interactive)
+  (if (chess-display-active-p)
+      (chess-game-run-hooks chess-module-game 'abort)
+    (ding)))
+
+(chess-message-catalog 'english
+  '((draw-offer . "You offer a draw")))
+
+(defun chess-display-draw ()
+  "Offer to draw the current game."
   (interactive)
   (if (chess-display-active-p)
-      (chess-game-resign chess-display-game)
+      (progn
+       (chess-message 'draw-offer)
+       (chess-game-run-hooks chess-module-game 'draw))
+    (ding)))
+
+(defun chess-display-undo (count)
+  "Abort the current game."
+  (interactive "P")
+  (if (chess-display-active-p)
+      (progn
+       ;; we can't call `chess-game-undo' directly, because not all
+       ;; engines will accept it right away!  So we just signal the
+       ;; desire to undo
+       (setq count
+             (if count
+                 (prefix-numeric-value count)
+               (if (eq (chess-pos-side-to-move (chess-display-position nil))
+                       (chess-game-data chess-module-game 'my-color))
+                   2 1)))
+       (chess-game-run-hooks chess-module-game 'undo count))
     (ding)))
 
 (defun chess-display-list-buffers ()
   "List all buffers related to this display's current game."
   (interactive)
-  (when chess-display-game
-    (let ((buffer-list-func (symbol-function 'buffer-list)))
-      (unwind-protect
-         (let ((chess-game chess-display-game)
-               (lb-command (lookup-key ctl-x-map [(control ?b)]))
-               (ibuffer-maybe-show-regexps nil))
-           (fset 'buffer-list
-                 (function
-                  (lambda ()
-                    (delq nil
-                          (mapcar (function
-                                   (lambda (cell)
-                                     (and (bufferp (cdr cell))
-                                          (buffer-live-p (cdr cell))
-                                          (cdr cell))))
-                                  (chess-game-hooks chess-game))))))
-           (call-interactively lb-command))
-       (fset 'buffer-list buffer-list-func)))))
+  (let ((buffer-list-func (symbol-function 'buffer-list)))
+    (unwind-protect
+       (let ((chess-game chess-module-game)
+             (lb-command (lookup-key ctl-x-map [(control ?b)]))
+             (ibuffer-maybe-show-regexps nil))
+         (fset 'buffer-list
+               (function
+                (lambda ()
+                  (delq nil
+                        (mapcar (function
+                                 (lambda (cell)
+                                   (and (bufferp (cdr cell))
+                                        (buffer-live-p (cdr cell))
+                                        (cdr cell))))
+                                (chess-game-hooks chess-game))))))
+         (call-interactively lb-command))
+      (fset 'buffer-list buffer-list-func))))
+
+(chess-message-catalog 'english
+  '((return-to-current . "Use '>' to return to the current position")))
 
 (defun chess-display-set-current (dir)
   "Change the currently displayed board.
@@ -624,211 +961,160 @@ to the end or beginning."
                     ((eq dir t) nil)
                     ((eq dir nil) 0))))
     (chess-display-set-index
-     nil (or index
-            (if chess-display-game
-                (chess-game-index chess-display-game)
-              (chess-var-index chess-display-variation))))
+     nil (or index (chess-game-index chess-module-game)))
     (unless (chess-display-active-p)
-      (message "Use '>' to return to the current position"))))
+      (chess-message 'return-to-current))))
 
 (defun chess-display-move-backward ()
   (interactive)
-  (if chess-display-index
-      (chess-display-set-current ?-)))
+  (chess-display-set-current ?-))
 
 (defun chess-display-move-forward ()
   (interactive)
-  (if chess-display-index
-      (chess-display-set-current ?+)))
+  (chess-display-set-current ?+))
 
 (defun chess-display-move-first ()
   (interactive)
-  (if chess-display-index
-      (chess-display-set-current nil)))
+  (chess-display-set-current nil))
 
 (defun chess-display-move-last ()
   (interactive)
-  (if chess-display-index
-      (chess-display-set-current t)))
+  (chess-display-set-current t))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
 ;; chess-display-edit-mode (for editing the position directly)
 ;;
 
+(defvar chess-display-edit-position nil)
+
+(make-variable-buffer-local 'chess-display-edit-position)
+
 (defvar chess-display-edit-mode-map
   (let ((map (make-keymap)))
     (suppress-keymap map)
-    (set-keymap-parent map chess-display-mode-map)
+
+    (define-key map [(control ?l)] 'chess-display-redraw)
+    (define-key map [(control ?i)] 'chess-display-invert)
+    (define-key map [tab] 'chess-display-invert)
+
+    (define-key map [??] 'describe-mode)
+    (define-key map [?L] 'chess-display-list-buffers)
+    ;;(define-key map [?C] 'chess-display-duplicate)
+    (define-key map [?I] 'chess-display-invert)
 
     (define-key map [?C] 'chess-display-clear-board)
     (define-key map [?G] 'chess-display-restore-board)
     (define-key map [?S] 'chess-display-send-board)
+    (define-key map [?X] 'chess-display-quit)
 
     (let ((keys '(?  ?p ?r ?n ?b ?q ?k ?P ?R ?N ?B ?Q ?K)))
       (while keys
        (define-key map (vector (car keys)) 'chess-display-set-piece)
        (setq keys (cdr keys))))
+
+    (cond
+     ((featurep 'xemacs)
+      (define-key map [(button1)] 'chess-display-mouse-select-piece)
+      (define-key map [(button2)] 'chess-display-mouse-set-piece)
+      (define-key map [(button3)] 'chess-display-mouse-set-piece))
+     (t
+      (define-key map [down-mouse-1] 'chess-display-mouse-select-piece)
+      (define-key map [drag-mouse-1] 'chess-display-mouse-select-piece)
+
+      (define-key map [mouse-2] 'chess-display-mouse-set-piece)
+      (define-key map [down-mouse-2] 'chess-display-mouse-set-piece)
+      (define-key map [mouse-3] 'chess-display-mouse-set-piece)
+      (define-key map [down-mouse-3] 'chess-display-mouse-set-piece)))
+
     map)
   "The mode map used for editing a chessboard position.")
 
+(chess-message-catalog 'english
+  '((editing-directly
+     . "Now editing position directly, use S when complete...")
+    (clear-chessboard-q . "Really clear the chessboard? ")))
+
 (defun chess-display-edit-board ()
   "Setup the current board for editing."
   (interactive)
-  (setq chess-display-edit-mode t)
-  ;; Take us out of any game/ply/variation we might be looking at,
-  ;; since we are not moving pieces now, but rather placing them --
-  ;; for which purpose the movement keys can still be used.
-  (chess-display-set-position nil (chess-display-position nil))
-  ;; jww (2002-03-28): setup edit-mode keymap here
-  (message "Now editing position directly, use S when complete..."))
+  (setq chess-display-edit-position
+       (chess-pos-copy (chess-display-position nil))
+       chess-display-edit-mode t
+       chess-display-side-to-move (chess-string 'mode-edit))
+  (force-mode-line-update)
+  (use-local-map chess-display-edit-mode-map)
+  (funcall chess-display-event-handler 'start-edit)
+  (chess-message 'editing-directly))
+
+(defun chess-display-end-edit-mode ()
+  (setq chess-display-edit-mode nil)
+  (funcall chess-display-event-handler 'end-edit)
+  (use-local-map chess-display-mode-map))
 
 (defun chess-display-send-board ()
   "Send the current board configuration to the user."
   (interactive)
-  (if chess-display-game
-      (chess-game-set-start-position chess-display-game
-                                    (chess-display-position nil)))
-  (setq chess-display-edit-mode nil))
+  (chess-display-end-edit-mode)
+  (chess-game-set-start-position chess-module-game
+                                chess-display-edit-position))
 
 (defun chess-display-restore-board ()
   "Setup the current board for editing."
   (interactive)
-  ;; jww (2002-03-28): NYI
-  (setq chess-display-edit-mode nil)
+  (chess-display-end-edit-mode)
+  ;; reset the modeline
+  (chess-display-set-index* nil chess-display-index)
   (chess-display-update nil))
 
 (defun chess-display-clear-board ()
   "Setup the current board for editing."
   (interactive)
-  (when (y-or-n-p "Really clear the chessboard? ")
+  (when (y-or-n-p (chess-string 'clear-chessboard-q))
     (let ((position (chess-display-position nil)))
       (dotimes (rank 8)
        (dotimes (file 8)
          (chess-pos-set-piece position (cons rank file) ? ))))
     (chess-display-update nil)))
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Allow for quick entry of algebraic moves via keyboard
-;;
-
-(defvar chess-move-string "")
-(defvar chess-legal-moves-pos nil)
-(defvar chess-legal-moves nil)
-
-(make-variable-buffer-local 'chess-move-string)
-(make-variable-buffer-local 'chess-legal-moves-pos)
-(make-variable-buffer-local 'chess-legal-moves)
-
-(defun chess-keyboard-test-move (move)
-  "Return the given MOVE if it matches the user's current input."
-  (let ((i 0) (x 0)
-       (l (length move))
-       (xl (length chess-move-string))
-       (match t))
-    (unless (or (and (equal chess-move-string "ok")
-                    (equal move "O-O"))
-               (and (equal chess-move-string "oq")
-                    (equal move "O-O-O")))
-      (while (and (< i l) (< x xl))
-       (if (= (aref move i) ?x)
-           (setq i (1+ i)))
-       (if (/= (downcase (aref move i))
-               (aref chess-move-string x))
-           (setq match nil i l)
-         (setq i (1+ i) x (1+ x)))))
-    (if match move)))
-
-(defsubst chess-keyboard-display-moves (&optional move-list)
-  (if (> (length chess-move-string) 0)
-      (message "[%s] %s" chess-move-string
-              (mapconcat 'identity
-                         (or move-list
-                             (delq nil (mapcar 'chess-keyboard-test-move
-                                               chess-legal-moves))) " "))))
-
-(defun chess-keyboard-shortcut-delete ()
-  (interactive)
-  (when (and chess-move-string
-            (stringp chess-move-string)
-            (> (length chess-move-string) 1))
-    (setq chess-move-string
-         (substring chess-move-string 0
-                    (1- (length chess-move-string))))
-    (chess-keyboard-display-moves)))
-
-(defun chess-keyboard-shortcut (&optional display-only)
-  (interactive)
-  (unless (memq last-command '(chess-keyboard-shortcut
-                              chess-keyboard-shortcut-delete))
-    (setq chess-move-string nil))
-  (unless display-only
-    (setq chess-move-string
-         (concat chess-move-string
-                 (char-to-string (downcase last-command-char)))))
-  (let ((position (chess-display-position nil)))
-    (unless (and chess-legal-moves
-                (eq position chess-legal-moves-pos))
-      (setq chess-legal-moves-pos position
-           chess-legal-moves
-           (sort (mapcar 'chess-ply-to-algebraic (chess-legal-plies position))
-                 'string-lessp)))
-    (let ((moves (delq nil (mapcar 'chess-keyboard-test-move
-                                  chess-legal-moves))))
-      (cond
-       ((= (length moves) 1)
-       (let ((ply (chess-algebraic-to-ply (chess-display-position nil)
-                                          (car moves))))
-         (unless ply
-           (error "Illegal move notation: %s" (car moves)))
-         (chess-display-move nil ply))
-       (setq chess-move-string nil
-             chess-legal-moves nil
-             chess-legal-moves-pos nil))
-       ((null moves)
-       (chess-keyboard-shortcut-delete))
-       (t
-       (chess-keyboard-display-moves moves))))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Manage a face cache for textual displays
-;;
-
-(defvar chess-display-face-cache '((t . t)))
-
-(defsubst chess-display-get-face (color)
-  (or (cdr (assoc color chess-display-face-cache))
-      (let ((face (make-face 'chess-display-highlight)))
-       (set-face-attribute face nil :background color)
-       (add-to-list 'chess-display-face-cache (cons color face))
-       face)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Default window and frame popup functions
-;;
+(defun chess-display-set-piece (&optional piece)
+  "Set the piece under point to command character, or space for clear."
+  (interactive)
+  (if (or (null piece) (char-valid-p piece))
+      (let ((index (get-text-property (point) 'chess-coord)))
+       (chess-pos-set-piece chess-display-edit-position index
+                            (or piece last-command-char))
+       (funcall chess-display-event-handler 'draw-square
+                (point) (or piece last-command-char) index))))
 
-(defun chess-display-popup-in-window ()
-  "Popup the given DISPLAY, so that it's visible to the user."
-  (unless (get-buffer-window (current-buffer))
-    (fit-window-to-buffer (display-buffer (current-buffer)))))
+(unless (fboundp 'event-window)
+  (defalias 'event-point 'ignore))
 
-(defun chess-display-popup-in-frame (display height width)
-  "Popup the given DISPLAY, so that it's visible to the user."
-  (let ((window (get-buffer-window (current-buffer) t)))
-    (if window
-       (let ((frame (window-frame window)))
-         (unless (eq frame (selected-frame))
-           (raise-frame frame)))
-      (let ((params (list (cons 'name "*Chessboard*")
-                         (cons 'height height)
-                         (cons 'width width))))
-       (if display
-           (push (cons 'display display) params))
-       (select-frame (make-frame params))
-       (set-window-dedicated-p (selected-window) t)))))
+(defun chess-display-mouse-set-piece (event)
+  "Select the piece the user clicked on."
+  (interactive "e")
+  (if (fboundp 'event-window)          ; XEmacs
+      (progn
+       (set-buffer (window-buffer (event-window event)))
+       (and (event-point event) (goto-char (event-point event))))
+    (set-buffer (window-buffer (posn-window (event-start event))))
+    (goto-char (posn-point (event-start event))))
+  (let ((pieces (if (memq (car event) '(down-mouse-3 mouse-3))
+                   '("Set black piece"
+                     ("Pieces"
+                      ("Pawn"   . ?p)
+                      ("Knight" . ?n)
+                      ("Bishop" . ?b)
+                      ("Queen"  . ?q)
+                      ("King"   . ?k)))
+                 '("Set white piece"
+                   ("Pieces"
+                    ("Pawn"   . ?P)
+                    ("Knight" . ?N)
+                    ("Bishop" . ?B)
+                    ("Queen"  . ?Q)
+                    ("King"   . ?K))))))
+    (chess-display-set-piece (x-popup-menu t pieces))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
@@ -836,59 +1122,122 @@ to the end or beginning."
 ;;
 
 (defvar chess-display-last-selected nil)
+
 (make-variable-buffer-local 'chess-display-last-selected)
 
+(chess-message-catalog 'english
+  '((cannot-mount   . "You cannot move pieces on top of each other")
+    (move-not-legal . "That is not a legal move")
+    (not-your-move  . "It is not your turn to move")
+    (wrong-color    . "You cannot move your opponent's pieces")
+    (selected-empty . "You cannot select an empty square")
+    (piece-immobile . "That piece cannot move now")))
+
 (defun chess-display-select-piece ()
   "Select the piece under the cursor.
 Clicking once on a piece selects it; then click on the target location."
   (interactive)
   (let ((coord (get-text-property (point) 'chess-coord))
-       (position (chess-display-position nil)))
+       (position (chess-display-position nil))
+       message)
     (when coord
-      (catch 'invalid
-       (if chess-display-last-selected
-           (let ((last-sel chess-display-last-selected))
-             ;; if they select the same square again, just deselect it
-             (if (= (point) (car last-sel))
-                 (chess-display-update nil)
-               (let ((s-piece (chess-pos-piece position (cadr last-sel)))
-                     (t-piece (chess-pos-piece position coord)) ply)
-                 (when (and (not (eq t-piece ? ))
-                            (if (chess-pos-side-to-move position)
-                                (< t-piece ?a)
-                              (> t-piece ?a)))
-                   (message "Cannot capture your own pieces.")
-                   (throw 'invalid t))
-                 (setq ply (chess-ply-create position (cadr last-sel) coord))
-                 (unless ply
-                   (message "That piece cannot move there in this position.")
-                   (throw 'invalid t))
-                 (chess-display-move nil ply)))
-             (setq chess-display-last-selected nil))
-         (let ((piece (chess-pos-piece position coord)))
-           (cond
-            ((eq piece ? )
-             (message "Cannot select an empty square.")
-             (throw 'invalid t))
-            ((if (chess-pos-side-to-move position)
-                 (> piece ?a)
-               (< piece ?a))
-             (message "Cannot move your opponent's pieces.")
-             (throw 'invalid t)))
-           (setq chess-display-last-selected (list (point) coord))
-           (chess-display-highlight nil coord 'selected)))))))
+      (setq message
+           (catch 'message
+             (if chess-display-last-selected
+                 (let ((last-sel chess-display-last-selected))
+                   ;; if they select the same square again, just deselect
+                   ;; it by redrawing the display and removing all
+                   ;; highlights
+                   (if (= (point) (car last-sel))
+                       (funcall chess-display-event-handler 'draw-square
+                                (car last-sel)
+                                (chess-pos-piece position (cdr last-sel))
+                                (cdr last-sel))
+                     (let ((s-piece (chess-pos-piece position (cdr last-sel)))
+                           (t-piece (chess-pos-piece position coord)) ply)
+                       (if chess-display-edit-mode
+                           (progn
+                             (chess-pos-set-piece position (cdr last-sel) ? )
+                             (chess-pos-set-piece position coord s-piece)
+                             (chess-display-update nil))
+                         (if (and (/= t-piece ? )
+                                  (or (and (< t-piece ?a)
+                                           (< s-piece ?a))
+                                      (and (> t-piece ?a)
+                                           (> s-piece ?a))))
+                             (throw 'message (chess-string 'cannot-mount)))
+                         (unless (setq ply (chess-ply-create position nil
+                                                             (cdr last-sel)
+                                                             coord))
+                           (throw 'message (chess-string 'move-not-legal)))
+                         (condition-case err
+                             (chess-display-move nil ply
+                                                 (car last-sel) (point))
+                           (error
+                            (throw 'message (error-message-string err)))))))
+                   (setq chess-display-last-selected nil))
+               (let ((piece (chess-pos-piece position coord)))
+                 (cond
+                  ((eq piece ? )
+                   (throw 'message (chess-string 'selected-empty)))
+                  ((not (or chess-display-edit-mode
+                            (not (chess-display-active-p))
+                            (eq (chess-pos-side-to-move position)
+                                (chess-game-data chess-module-game
+                                                 'my-color))))
+                   (throw 'message (chess-string 'not-your-move)))
+                  ((and (not chess-display-edit-mode)
+                        (if (chess-pos-side-to-move position)
+                            (> piece ?a)
+                          (< piece ?a)))
+                   (throw 'message (chess-string 'wrong-color)))
+                  ((and (not chess-display-edit-mode)
+                        chess-display-highlight-legal
+                        (null (chess-legal-plies position :any :index coord)))
+                   (throw 'message (chess-string 'piece-immobile))))
+                 (setq chess-display-last-selected (cons (point) coord))
+                 (chess-display-highlight nil coord)
+                 (if (and (not chess-display-edit-mode)
+                          chess-display-highlight-legal)
+                     (chess-display-highlight-legal nil coord))))))
+      (when message
+       (when chess-display-last-selected
+         (funcall chess-display-event-handler 'draw-square
+                  (car chess-display-last-selected)
+                  (chess-pos-piece position
+                                   (cdr chess-display-last-selected))
+                  (cdr chess-display-last-selected))
+         (setq chess-display-last-selected nil))
+       (message message)))))
 
 (defun chess-display-mouse-select-piece (event)
   "Select the piece the user clicked on."
   (interactive "e")
-  (cond ((fboundp 'event-window)       ; XEmacs
-        (set-buffer (window-buffer (event-window event)))
-        (and (event-point event) (goto-char (event-point event))))
-       ((fboundp 'posn-window)         ; Emacs
-        (set-buffer (window-buffer (posn-window (event-start event))))
-        (goto-char (posn-point (event-start event)))))
+  (if (fboundp 'event-window)          ; XEmacs
+      (progn
+       (set-buffer (window-buffer (event-window event)))
+       (and (event-point event) (goto-char (event-point event))))
+    (if (equal (event-start event) (event-end event))
+       (progn
+         (set-buffer (window-buffer (posn-window (event-start event))))
+         (goto-char (posn-point (event-start event))))
+      (goto-char (posn-point (event-end event)))))
   (chess-display-select-piece))
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Maintain a face cache for given color strings
+;;
+
+(defvar chess-display-face-cache '((t . t)))
+
+(defun chess-display-get-face (color)
+  (or (cdr (assoc color chess-display-face-cache))
+      (let ((face (make-face 'chess-display-highlight)))
+       (set-face-attribute face nil :background color)
+       (add-to-list 'chess-display-face-cache (cons color face))
+       face)))
+
 (provide 'chess-display)
 
 ;;; chess-display.el ends here