scel/el/sclang-server.el

299 lines
10 KiB
EmacsLisp

;;; sclang-server.el --- IDE for working with SuperCollider -*- coding: utf-8;
;;
;; Copyright 2003 stefan kersten <steve@k-hornz.de>
;;; License:
;; This program 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 of the
;; License, or (at your option) any later version.
;;
;; This program 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 this program; if not, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
;; USA
;;; Commentary:
;; Interface to the sclang server
(require 'cl-lib)
(require 'sclang-util)
(require 'sclang-interp)
(require 'sclang-language)
(require 'sclang-mode)
;;; Code:
(defcustom sclang-server-panel "Server.default.makeWindow"
"Expression to execute when `sclang-show-server-panel' is invoked."
:group 'sclang-interface
:type '(choice (const "Server.default.makeWindow")
(const "\\SCUM.asClass.do { \\SCUM.asClass.desktop.showServerPanel }")
string))
(defvar sclang-server-alist nil
"Alist of currently defined synthesis servers.")
(defvar sclang-default-server nil
"Default synthesis server.")
(defvar sclang-current-server nil
"Currently selected synthesis server.")
(defvar sclang-current-server-initialized nil
"Non nil when the current server has been initialized from the default server.")
(defconst sclang-server-running-face
(let ((face (make-face 'sclang-server-running-face)))
(set-face-foreground face "red")
face)
"Face for highlighting a server's running state in the mode-line.")
(defun sclang-get-server (&optional name)
"Get sclang server (optionally by NAME)."
(unless name (setq name sclang-current-server))
(cdr (assq name sclang-server-alist)))
(defun sclang-set-server (&optional name)
"Set current sclang server (optionally by NAME)."
(unless name (setq name sclang-current-server))
(setq sclang-current-server
(car (or (assq name sclang-server-alist)
(car sclang-server-alist)))))
(sclang-set-command-handler
'_updateServer
(lambda (arg)
(setq sclang-server-alist
(sort (cdr arg) (lambda (a b) (string-lessp (car a) (car b)))))
(setq sclang-default-server (car arg))
(unless sclang-current-server-initialized
;; only set the current server automatically once after startup
(setq sclang-current-server-initialized t)
(sclang-set-server sclang-default-server))
(sclang-update-server-info)))
(defun sclang-next-server ()
"Select next server for display."
(interactive)
(sclang-set-server)
(let ((list (or (cdr (cl-member-if (lambda (assoc)
(eq (car assoc) sclang-current-server))
sclang-server-alist))
sclang-server-alist)))
(setq sclang-current-server (car (car list))))
(sclang-update-server-info))
(defun sclang-mouse-next-server (_event)
"Select next server for display."
(interactive "e")
(sclang-next-server))
(defun sclang-server-running-p (&optional name)
"Is the sclang server NAME running?"
(plist-get (sclang-get-server name) 'running))
(defun sclang-server-booting-p (&optional name)
"Is the sclang server NAME running?"
(plist-get (sclang-get-server name) 'booting))
(defun sclang-create-server-menu (title)
"Create the server menu with TITLE."
(easy-menu-create-menu
title
'(
["Boot" sclang-server-boot]
["Quit" sclang-server-quit]
"-"
["Free All" sclang-server-free-all :active (sclang-server-running-p)]
["Make Default" sclang-server-make-default])))
(defun sclang-server-fill-mouse-map (map prefix)
"Fill mouse MAP using PREFIX."
(define-key map (vector prefix 'mouse-1) 'sclang-mouse-next-server)
(define-key map (vector prefix 'down-mouse-3) (sclang-create-server-menu "Commands"))
map)
(defvar sclang-server-mouse-map (sclang-server-fill-mouse-map (make-sparse-keymap) 'mode-line)
"Keymap used for controlling servers in the mode line.")
(defun sclang-server-fill-key-map (map)
"Fill server keymap MAP."
(define-key map [?b] 'sclang-server-boot)
(define-key map [?d] 'sclang-server-display-default)
(define-key map [?f] 'sclang-server-free-all)
(define-key map [?m] 'sclang-server-make-default)
(define-key map [?n] 'sclang-next-server)
(define-key map [?o] 'sclang-server-dump-osc)
(define-key map [?p] 'sclang-show-server-panel)
(define-key map [?q] 'sclang-server-quit)
(cl-flet ((fill-record-map (map)
(define-key map [?n] 'sclang-server-prepare-for-record)
(define-key map [?p] 'sclang-server-pause-recording)
(define-key map [?r] 'sclang-server-record)
(define-key map [?s] 'sclang-server-stop-recording)
map))
(define-key map [?r] (fill-record-map (make-sparse-keymap))))
map)
(defvar sclang-server-key-map (sclang-server-fill-key-map (make-sparse-keymap))
"Keymap used for controlling servers.")
(defun sclang-get-server-info-string ()
"Return a mode-line string for the current server."
(let* ((name (if sclang-current-server (symbol-name sclang-current-server) "-------"))
(server (sclang-get-server))
(running-p (if server (plist-get server 'running)))
(string (propertize
name
'face (if running-p sclang-server-running-face)
'help-echo "mouse-1: next server, mouse-3: command menu"
'keymap sclang-server-mouse-map))
;; (make-mode-line-mouse-map 'mouse-1 'sclang-mouse-next-server)))
(address (if (and server (not (eq (plist-get server 'type) 'internal)))
(format " (%s)" (plist-get server 'address))
""))
(info (if running-p
(mapcar 'number-to-string
(plist-get (sclang-get-server) 'info))
'("---" "---" "----" "----" "----" "----"))))
(apply 'format "%s%s %3s|%3s %% u: %4s s: %4s g: %4s d: %4s" string address info)))
(defvar sclang-server-info-string (sclang-get-server-info-string)
"Info string used in the post buffer mode line.")
(defun sclang-update-server-info ()
"Update server info in the modeline."
(interactive)
(sclang-set-server)
(setq sclang-server-info-string (sclang-get-server-info-string))
(force-mode-line-update t))
;; =====================================================================
;; language control
;; =====================================================================
(defun sclang-perform-server-command (command &rest args)
"Perform server COMMAND with ARGS."
(sclang-eval-string
(sclang-format
"Server.named.at(%o.asSymbol).performList(\\tryPerform, %o.asSymbol.asArray ++ %o)"
sclang-current-server command args)
nil))
(defun sclang-server-boot ()
"Boot the current server."
(interactive)
(sclang-perform-server-command "boot"))
(defun sclang-server-reboot ()
"Reboot the current server."
(interactive)
(sclang-perform-server-command "reboot"))
(defun sclang-server-quit ()
"Quit the current server."
(interactive)
(sclang-perform-server-command "quit"))
(defun sclang-server-free-all ()
"Free all nodes on the current server."
(interactive)
(sclang-perform-server-command "freeAll"))
(defun sclang-server-display-default ()
"Display default server."
(interactive)
(when sclang-default-server
(sclang-set-server sclang-default-server)
(sclang-update-server-info)))
(defun sclang-server-make-default ()
"Make current server the default server."
(interactive)
(when sclang-current-server
(sclang-eval-string
(sclang-format "
var server;
server = Server.named.at(%o);
if (server.notNil) {
Server.default = server;
thisProcess.interpreter.s = server;
\"Default server: %\n\".postf(server.name);
};
" sclang-current-server))
nil))
(defun sclang-server-dump-osc (&optional code)
"Set the current server's dump OSC mode (with optional CODE)."
(interactive "P")
(sclang-perform-server-command "dumpOSC"
(cond ((consp code) 0)
((null code) 1)
(t code))))
(defun sclang-server-prepare-for-record (&optional path)
"Prepare current server for recording a sound file (with optional PATH)."
(interactive
(list
(and current-prefix-arg (read-file-name "Record to file: "))))
(sclang-perform-server-command "prepareForRecord" path))
(defun sclang-server-record ()
"Record a sound file on the current server."
(interactive)
(sclang-perform-server-command "record"))
(defun sclang-server-pause-recording ()
"Pause recording on the current server."
(interactive)
(sclang-perform-server-command "pauseRecording"))
(defun sclang-server-stop-recording ()
"Stop recording on the current server."
(interactive)
(sclang-perform-server-command "stopRecording"))
(defun sclang-set-server-latency (latency)
"Set the current server's LATENCY instance variable."
(interactive "nSet latency: ")
(sclang-perform-server-command "latency_" latency))
(defun sclang-show-server-latency ()
"Show the current server's latency."
(interactive)
(let ((server (sclang-get-server)))
(message "%s" (and server (plist-get server 'latency)))))
(defun sclang-show-server-panel ()
"Show graphical server panel if available."
(interactive)
(sclang-eval-string sclang-server-panel))
;; =====================================================================
;; module setup
;; =====================================================================
(add-hook 'sclang-mode-hook
(lambda ()
;; install server mode line in post buffer
(when (string= (buffer-name) sclang-post-buffer)
(setq mode-line-format '("-" sclang-server-info-string)))
;; install server prefix keymap
(define-key sclang-mode-map "\C-c\C-p" sclang-server-key-map)))
(add-hook 'sclang-library-shutdown-hook
(lambda ()
(setq sclang-current-server-initialized nil)))
(provide 'sclang-server)
;;; sclang-server.el ends here