;;; emacspeak-remote.el --- Enables running remote Emacspeak sessions
;;; $Id: emacspeak-remote.el 8146 2013-02-09 20:05:08Z tv.raman.tv $
;;; $Author: tv.raman.tv $
;;; Description: Auditory interface to remote speech server
;;; Keywords: Emacspeak, Speak, Spoken Output, remote server
;;{{{  LCD Archive entry:

;;; LCD Archive Entry:
;;; emacspeak| T. V. Raman |raman@cs.cornell.edu
;;; A speech interface to Emacs |
;;; $Date: 2008-07-25 16:05:19 -0700 (Fri, 25 Jul 2008) $ |
;;;  $Revision: 4532 $ |
;;; Location undetermined
;;;

;;}}}
;;{{{  Copyright:

;;; Copyright (c) 1995 -- 2011, T. V. Raman
;;; All Rights Reserved.
;;;
;;; This file is not part of GNU Emacs, but the same permissions apply.
;;;
;;; GNU Emacs is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2, or (at your option)
;;; any later version.
;;;
;;; GNU Emacs is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Emacs; see the file COPYING.  If not, write to
;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

;;}}}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;{{{  Required modules
(require 'emacspeak-preamble)
;;}}}
;;{{{  Introduction

;;; In a running emacspeak session,
;;;nuke the running server and start talking to a remote speech server,
;;;after prompting for host and port

;;}}}
;;{{{  User customizations
;;;###autoload
(defgroup emacspeak-remote nil
  "Emacspeak remote group."
  :group 'emacspeak-remote)
;;;###autoload
(defcustom emacspeak-remote-hooks nil
  "List of hook functions that are run after
emacspeak is set to run as a remote application.
Use this to add actions you typically perform after you enter remote
mode."
  :type 'hook
  :group 'emacspeak-remote)

;;; Here is what I currently use:
;;; It switches to using C-r as the emacspeak prefix key
;;; if emacspeak-remote-update-keymap is set to t
(defvar emacspeak-remote-update-keymap nil
  "*Set this to T if you want the default remote startup hook
to update your keymap.
This is useful if you run remote emacspeak sessions within
a local  Emacspeak terminal buffer.")

(defun emacspeak-remote-default-hook ()
  "Function run by default  when we launch a remote session"
  (declare (special emacspeak-remote-update-keymap
                    emacspeak-auditory-icon-function))
  (when emacspeak-remote-update-keymap
    (emacspeak-keymap-choose-new-emacspeak-prefix
     (format "%c" 18)))
  (setq emacspeak-auditory-icon-function
        'emacspeak-serve-auditory-icon))

(add-hook 'emacspeak-remote-hooks 'emacspeak-remote-default-hook)
;;}}}
;;{{{ Helper for guessing host where we came from:
;;; see etc/last-log.pl

;;;Remote hostname guessing
;;;
(declaim (special emacspeak-resource-directory))

(defvar emacspeak-remote-hostname
  (expand-file-name  ".current-remote-hostname"
                     emacspeak-resource-directory)
  "Filename containing the name of the host we connected from")

;;;###autoload
(defun emacspeak-remote-edit-current-remote-hostname  ()
  "Interactively set up where we came from.
Value is persisted for use with ssh servers."
  (interactive)
  (declare (special emacspeak-remote-hostname))
  (when (file-exists-p   emacspeak-remote-hostname )
    (find-file emacspeak-remote-hostname)))
;;; Todo: parse out hostname if the file has user@host:port
(defun emacspeak-remote-get-current-remote-hostname  ()
  "Return the name of the remote hostname from where we connected if known"
  (declare (special emacspeak-remote-hostname))
  (when (file-exists-p   emacspeak-remote-hostname )
    (let ((buffer (find-file-noselect emacspeak-remote-hostname))
          (result nil))
      (save-excursion
        (set-buffer buffer)
        (setq result (buffer-substring (point-min) (1- (point-max)))))
      (kill-buffer buffer )
      result)))

;;}}}
;;{{{  Connect to  remote server
(defun emacspeak-remote-auto-connect-to-server ()
  "Invoked via gnudoit --typically from a login sequence--
to have the running emacspeak connect to a server running on
the host we just logged in from."
  (let ((host (emacspeak-remote-get-current-remote-hostname)))
    (when (and  (> (length host) 0)
                (not (eq host (system-name))))
      (emacspeak-remote-quick-connect-to-server))))

(defvar emacspeak-remote-default-port-to-connect
  "2222"
  "Default used when prompting for a port to connect to.")

;;;###autoload

;;;###autoload
(defun emacspeak-remote-quick-connect-to-server()
  "Connect to remote server.
Does not prompt for host or port, but quietly uses the guesses
that appear as defaults when prompting. Use this once you are
sure the guesses are usually correct."
  (interactive)
  (emacspeak-remote-connect-to-server
   (emacspeak-remote-get-current-remote-hostname)
   (string-to-number  emacspeak-remote-default-port-to-connect)))

;;;###autoload
(defun emacspeak-remote-home()
  "Open ssh session to where we came from.
Uses value returned by `emacspeak-remote-get-current-remote-hostname'."
  (interactive)
  (unless (require 'ssh)
    (error "You do not have module ssh.el installed."))
  (let
      ((spec (emacspeak-remote-get-current-remote-hostname))
       fields host port user)
    (setq fields (split-string spec "[@:]"))
    (setq
     user  (first fields)
     host (second fields)
     port (third fields))
    (ssh
     (format "%s -p %s -l %s"
             host port user)
     (format "Remote-%s"
             host))))

;;;###autoload
(defun emacspeak-remote-ssh-to-server(login host port)
  "Open ssh session to where we came from."
  (interactive
   (list
    (read-from-minibuffer "Login: "
                          (user-login-name))
    (read-from-minibuffer "Host: ")
    (read-from-minibuffer "Port: "
                          "22")))
  (unless (require 'ssh)
    (error "You do not have module ssh.el installed."))
  (ssh
   (format "%s -p %s -l %s"
           host port login)
   "remote-ssh"))

;;;###autoload
(defcustom emacspeak-remote-default-ssh-server
  nil
  "Default ssh server to use for remote speech server."
  :type '(choice
          (const  :tag "Ignore" nil)
          (string  :tag "SSH Server"))
  :group 'emacspeak-remote)

;;;###autoload
(defun emacspeak-remote-quick-connect-via-ssh ()
  "Connect via ssh to remote Emacspeak server.
Server is specified via custom option `emacspeak-remote-default-ssh-server'."
  (interactive)
  (declare (special emacspeak-remote-default-ssh-server))
  (when emacspeak-remote-default-ssh-server
    (setq dtk-program emacspeak-remote-default-ssh-server)
    (dtk-select-server emacspeak-remote-default-ssh-server)
    (dtk-initialize)))

;;;###autoload
(defun  emacspeak-remote-connect-to-server (host port)
  "Connect to and start using remote speech server running on
host host and listening on port port. Host is the hostname of the
remote server, typically the desktop machine. Port is the tcp
port that that host is listening on for speech requests."
  (interactive
   (list
    (completing-read "Remote host: "
                     emacspeak-eterm-remote-hosts-table ;completion table
                     nil                ;predicate
                     nil                ;must-match
                     (emacspeak-remote-get-current-remote-hostname) ;initial input
                     )
    (read-from-minibuffer "Remote port:" dtk-local-server-port)))
  (declare (special dtk-speaker-process dtk-program 
                    dtk-local-server-port
                    emacspeak-eterm-remote-hosts-table))
  (let* ((dtk-program dtk-local-engine)
         (process-connection-type nil)  ;dont waste a pty
         (old-process dtk-speaker-process)
         (new-process
          (open-network-stream "remote-speaker" nil host port)))
    (unless (intern-soft host emacspeak-eterm-remote-hosts-table)
      (emacspeak-eterm-cache-remote-host host))
    (accept-process-output)
    (cond
     ((or (eq 'run (process-status new-process))
          (eq 'open (process-status new-process)))
      (setq dtk-speaker-process new-process)
      (setq emacspeak-remote-default-port-to-connect (format "%s" port ))
      (delete-process old-process)
      (run-hooks 'emacspeak-remote-hooks)
      (emacspeak-tts-startup-hook)
      (message "Connecting to server on host %s  port %s" host port ))
     (t (error "Failed to connect to speech server on host %s port %s" host port )))))

;;}}}
(provide 'emacspeak-remote )
;;{{{ end of file

;;; local variables:
;;; folded-file: t
;;; byte-compile-dynamic: nil
;;; end:

;;}}}
