aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoakim Verona2013-08-19 20:48:07 +0200
committerJoakim Verona2013-08-19 20:48:07 +0200
commit0718a5a7c63d006beed121033dc37ee4f229bd24 (patch)
tree879684434bbd0b9cb77b0e6e21e976772cf1176d
parent0c1b014bcd625bb88708aff3c7d401aa3b88fa07 (diff)
parentedeb7fdc4fd422545044973b1f41309402f8bab8 (diff)
downloademacs-0718a5a7c63d006beed121033dc37ee4f229bd24.tar.gz
emacs-0718a5a7c63d006beed121033dc37ee4f229bd24.zip
Merge branch 'xwidget' of https://github.com/jave/xwidget-emacs into xwidget
-rw-r--r--lisp/emacs-parallel/README.org147
-rw-r--r--lisp/emacs-parallel/parallel-remote.el40
-rw-r--r--lisp/emacs-parallel/parallel-xwidget.el59
-rw-r--r--lisp/emacs-parallel/parallel.el41
-rw-r--r--lisp/xwidget.el6
-rw-r--r--test/automated/xwidget-tests.el13
6 files changed, 270 insertions, 36 deletions
diff --git a/lisp/emacs-parallel/README.org b/lisp/emacs-parallel/README.org
new file mode 100644
index 00000000000..743050518be
--- /dev/null
+++ b/lisp/emacs-parallel/README.org
@@ -0,0 +1,147 @@
1* Emacs Parallel
2
3 Emacs Parallel is yet another library to simulate parallel
4 computations in Emacs (because it lacks threads support in Elisp).
5
6* STARTED HowTo
7
8 You can execute a simple function a retrive the result like this:
9 #+BEGIN_SRC emacs-lisp
10 (parallel-get-result (parallel-start (lambda () (* 42 42))))
11 ⇒ 1764
12 #+END_SRC
13
14 Though you won't benefit from the parallelism because
15 ~parallel-get-result~ is blocking, that is it waits for the function
16 to be executed.
17
18 So you can use define a callback to be called when the function is
19 finished:
20 #+BEGIN_SRC emacs-lisp
21 (parallel-start (lambda () (sleep-for 4.2) "Hello World")
22 :post-exec (lambda (results _status)
23 (message (first results))))
24 ⊣ Hello World
25 #+END_SRC
26
27 Here, why ~(first results)~ and not ~result~? Because you can send
28 data from the remote instance while it's running with
29 ~parallel-remote-send~:
30 #+BEGIN_SRC emacs-lisp
31 (parallel-start (lambda ()
32 (parallel-remote-send "Hello")
33 (sleep-for 4.2)
34 "World")
35 :post-exec (lambda (results _status)
36 (message "%s"
37 (mapconcat #'identity (reverse results) " "))))
38 ⊣ Hello World
39 #+END_SRC
40 As you may have noticed the results are pushed in a list, so the
41 first element is the result returned by the function called, the
42 second is the last piece of data send, and so on...
43
44 And of course you can execute some code when you receive data from
45 the remote instance:
46 #+BEGIN_SRC emacs-lisp
47 (parallel-start (lambda ()
48 (parallel-remote-send 42)
49 (sleep-for 4.2) ; heavy computation to compute PI
50 pi)
51 :on-event (lambda (data)
52 (message "Received %S" data)))
53 ⊣ Received 42
54 ⊣ Received 3.141592653589793
55 #+END_SRC
56
57 Because the function is executed in another Emacs instance (in Batch
58 Mode by default), the environment isn't the same. However you can
59 send some data with the ~env~ parameter:
60 #+BEGIN_SRC emacs-lisp
61 (let ((a 42)
62 (b 12))
63 (parallel-get-result (parallel-start (lambda (a b) (+ a b))
64 :env (list a b))))
65 ⇒ 54
66 #+END_SRC
67
68 By default, the remote Emacs instance is exited when the function is
69 executed, but you can keep it running with the
70 ~:continue-when-executed~ option and send new code to be executed
71 with ~parellel-send~.
72 #+BEGIN_SRC emacs-lisp
73 (let ((task (parallel-start (lambda () 42)
74 :continue-when-executed t)))
75 (sleep-for 4.2)
76 (parallel-send task (lambda () (setq parallel-continue-when-executed nil) 12))
77 (parallel-get-results task))
78 ⇒ (12 42)
79 #+END_SRC
80
81 As you can see, to stop the remote instance you have to set the
82 variable ~parallel-continue-when-executed~ to nil.
83
84* Modules
85
86** Parallel XWidget
87
88 [[http://www.emacswiki.org/emacs/EmacsXWidgets][Emacs XWidget]] is an experimental branch which permits to embed GTK+
89 widget inside Emacs buffers. For instance, it is possible to use it
90 to render an HTML page using the webkit engine within an Emacs
91 buffer.
92
93 With this module, you can configure your "main" Emacs to use
94 another one to render web pages.
95
96 Let's assume that you've cloned [[https://github.com/jave/xwidget-emacs][the Emacs XWidget repository]] in
97 ~$HOME/src/emacs-xwidget/~. Once you've compiled it, an Emacs
98 executable is available ~$HOME/src/emacs-xwidget/src/emacs~.
99
100 Configure ~parallel-xwidget~ to use it:
101 #+BEGIN_SRC emacs-lisp
102 (setq parallel-xwidget-config (list :emacs-path
103 (concat (getenv "HOME")
104 "/src/emacs-xwidget/src/emacs")))
105 #+END_SRC
106
107 Then configure your current Emacs to use it:
108 #+BEGIN_SRC emacs-lisp
109 (setq browse-url-browser-function 'parallel-xwidget-browse-url)
110 #+END_SRC
111
112 You can check it out with M-x browse-url RET google.com RET.
113
114* Tips & Tricks
115
116 If your windows manager is smart enough (like StumpwWM) you can use
117 it to move graphical windows (Emacs frames) in another desktop.
118
119 For example, I use this to move Emacs frames (with the title
120 "emacs-debug") to the group (aka desktop) 9:
121 #+BEGIN_SRC lisp
122 (define-frame-preference "9"
123 (0 nil t :title "emacs-debug"))
124 #+END_SRC
125
126 And this to specify the title of the frame:
127 #+BEGIN_SRC emacs-lisp
128 (parallel-start (lambda () 42)
129 :no-batch t
130 :emacs-args '("-T" "emacs-debug"))
131 #+END_SRC
132
133* TODO How does it work?
134
135* Known limitations
136
137 You can only send data to the remote (with the ~env~ parameter) or
138 from the remote (with ~parallel-send~ and ~parallel-remote-send~)
139 that have a printed representation (see [[info:elisp#Printed%20Representation][info:elisp#Printed
140 Representation]]).
141
142 So you can pass around numbers, symbols, strings, lists, vectors,
143 hash-table but you can't pass buffers, windows, frames...
144
145
146 It lacks documentation, tests and probably a clean API, but I'm
147 working on it!
diff --git a/lisp/emacs-parallel/parallel-remote.el b/lisp/emacs-parallel/parallel-remote.el
index 5c24e55e089..54626afc267 100644
--- a/lisp/emacs-parallel/parallel-remote.el
+++ b/lisp/emacs-parallel/parallel-remote.el
@@ -22,12 +22,15 @@
22 22
23;;; Code: 23;;; Code:
24 24
25(require 'cl)
26
25(defvar parallel-service nil) 27(defvar parallel-service nil)
26(defvar parallel-task-id nil) 28(defvar parallel-task-id nil)
27(defvar parallel-client nil) 29(defvar parallel-client nil)
28(defvar parallel--executed nil) 30(defvar parallel--executed nil)
31(defvar parallel-continue-when-executed nil)
29 32
30(defun parallel-send (data) 33(defun parallel-remote-send (data)
31 (process-send-string parallel-client 34 (process-send-string parallel-client
32 (format "%S " (cons parallel-task-id data)))) 35 (format "%S " (cons parallel-task-id data))))
33 36
@@ -39,7 +42,7 @@
39 :host "localhost" 42 :host "localhost"
40 :family 'ipv4)) 43 :family 'ipv4))
41 (set-process-filter parallel-client #'parallel-remote--filter) 44 (set-process-filter parallel-client #'parallel-remote--filter)
42 (parallel-send 'code) 45 (parallel-remote-send 'code)
43 (when noninteractive ; Batch Mode 46 (when noninteractive ; Batch Mode
44 ;; The evaluation is done in the `parallel--filter' but in Batch 47 ;; The evaluation is done in the `parallel--filter' but in Batch
45 ;; Mode, Emacs doesn't wait for the input, it stops as soon as 48 ;; Mode, Emacs doesn't wait for the input, it stops as soon as
@@ -48,15 +51,30 @@
48 (sleep-for 10)))) ; arbitrary chosen 51 (sleep-for 10)))) ; arbitrary chosen
49 52
50(defun parallel-remote--filter (_proc output) 53(defun parallel-remote--filter (_proc output)
51 (parallel-send 54 (dolist (code (parallel--read-output output))
52 (if (or noninteractive 55 (parallel-remote-send
53 (not debug-on-error)) 56 (if (or noninteractive
54 (condition-case err 57 (not debug-on-error))
55 (eval (read output)) 58 (condition-case err
56 (error err)) 59 (eval code)
57 (eval (read output)))) 60 (error err))
58 (setq parallel--executed t) 61 (eval code))))
59 (kill-emacs)) 62 (unless parallel-continue-when-executed
63 (setq parallel--executed t)
64 (kill-emacs)))
65
66(defun parallel--read-output (output)
67 "Read lisp forms from output and return them as a list."
68 (loop with output = (replace-regexp-in-string
69 "\\`[ \t\n]*" ""
70 (replace-regexp-in-string "[ \t\n]*\\'" "" output)) ; trim string
71 with start = 0
72 with end = (length output)
73 for ret = (read-from-string output start end)
74 for data = (first ret)
75 do (setq start (rest ret))
76 collect data
77 until (= start end)))
60 78
61(provide 'parallel-remote) 79(provide 'parallel-remote)
62 80
diff --git a/lisp/emacs-parallel/parallel-xwidget.el b/lisp/emacs-parallel/parallel-xwidget.el
new file mode 100644
index 00000000000..7e23863d6eb
--- /dev/null
+++ b/lisp/emacs-parallel/parallel-xwidget.el
@@ -0,0 +1,59 @@
1;;; parallel-xwidget.el ---
2
3;; Copyright (C) 2013 Grégoire Jadi
4
5;; Author: Grégoire Jadi <gregoire.jadi@gmail.com>
6
7;; This program is free software: you can redistribute it and/or
8;; modify it under the terms of the GNU General Public License as
9;; published by the Free Software Foundation, either version 3 of
10;; the License, or (at your option) any later version.
11
12;; This program is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20;;; Commentary:
21
22;;; Code:
23
24(require 'parallel)
25(require 'browse-url)
26
27(defgroup parallel-xwidget nil
28 "Browse the web in another emacs instance with XWidget."
29 :group 'emacs)
30
31(defvar parallel-xwidget--task nil)
32
33(defcustom parallel-xwidget-config nil
34 "Parallel configuration."
35 :type 'alist
36 :group 'parallel-xwidget)
37
38(defun parallel-xwidget--init ()
39 (setq parallel-xwidget--task
40 (parallel-start (lambda ()
41 (require 'xwidget))
42 :graphical t
43 :continue-when-executed t
44 :config parallel-xwidget-config)))
45
46(defun parallel-xwidget-browse-url (url &optional new-session)
47 "Browse URL in another Emacs instance."
48 (interactive (browse-url-interactive-arg "xwidget-webkit URL: "))
49 (unless (and parallel-xwidget--task
50 (eq 'run (parallel-status parallel-xwidget--task)))
51 (parallel-xwidget--init))
52 (parallel-send parallel-xwidget--task
53 (lambda (url new-session)
54 (xwidget-webkit-browse-url url new-session))
55 (url-tidy url) new-session))
56
57(provide 'parallel-xwidget)
58
59;;; parallel-xwidget.el ends here
diff --git a/lisp/emacs-parallel/parallel.el b/lisp/emacs-parallel/parallel.el
index 89655af3ee8..3e5eccfd73c 100644
--- a/lisp/emacs-parallel/parallel.el
+++ b/lisp/emacs-parallel/parallel.el
@@ -23,7 +23,7 @@
23;;; Code: 23;;; Code:
24 24
25(require 'cl) 25(require 'cl)
26(require 'find-func) 26(require 'parallel-remote)
27 27
28(defgroup parallel nil 28(defgroup parallel nil
29 "Execute stuff in parallel" 29 "Execute stuff in parallel"
@@ -87,7 +87,7 @@
87 87
88(defun* parallel-start (exec-fun &key post-exec env timeout 88(defun* parallel-start (exec-fun &key post-exec env timeout
89 emacs-path library-path emacs-args 89 emacs-path library-path emacs-args
90 graphical debug on-event 90 graphical debug on-event continue-when-executed
91 username hostname hostport 91 username hostname hostport
92 config) 92 config)
93 (parallel--init-server) 93 (parallel--init-server)
@@ -101,6 +101,7 @@
101 graphical 101 graphical
102 debug 102 debug
103 on-event 103 on-event
104 continue-when-executed
104 username 105 username
105 hostname 106 hostname
106 hostport) 107 hostport)
@@ -113,7 +114,7 @@
113 library-path (or library-path 114 library-path (or library-path
114 (plist-get config :library-path) 115 (plist-get config :library-path)
115 (plist-get parallel-config :library-path) 116 (plist-get parallel-config :library-path)
116 (find-library-name "parallel-remote"))) 117 (locate-library "parallel-remote")))
117 118
118 (let ((task (parallel--new-task)) 119 (let ((task (parallel--new-task))
119 proc tunnel ssh-args) 120 proc tunnel ssh-args)
@@ -127,6 +128,7 @@
127 (put task 'on-event on-event)) 128 (put task 'on-event on-event))
128 (put task 'results nil) 129 (put task 'results nil)
129 (put task 'status 'run) 130 (put task 'status 'run)
131 (put task 'queue nil)
130 132
131 ;; We need to get the tunnel if it exists so we can send the right 133 ;; We need to get the tunnel if it exists so we can send the right
132 ;; `service' to the remote. 134 ;; `service' to the remote.
@@ -150,6 +152,7 @@
150 (process-contact parallel--server :service))) 152 (process-contact parallel--server :service)))
151 "--eval" (format "(setq parallel-task-id '%S)" task) 153 "--eval" (format "(setq parallel-task-id '%S)" task)
152 "--eval" (format "(setq debug-on-error '%S)" debug) 154 "--eval" (format "(setq debug-on-error '%S)" debug)
155 "--eval" (format "(setq parallel-continue-when-executed '%S)" continue-when-executed)
153 "-f" "parallel-remote--init" 156 "-f" "parallel-remote--init"
154 emacs-args))) 157 emacs-args)))
155 158
@@ -239,23 +242,20 @@ to `funcall' FUN with ENV as arguments."
239(defun parallel--filter (connection output) 242(defun parallel--filter (connection output)
240 "Server filter used to retrieve the results send by the remote 243 "Server filter used to retrieve the results send by the remote
241process and send the code to be executed by it." 244process and send the code to be executed by it."
242 (loop with output = (replace-regexp-in-string 245 (dolist (data (parallel--read-output output))
243 "\\`[ \t\n]*" "" 246 (parallel--process-output connection (first data) (rest data))))
244 (replace-regexp-in-string "[ \t\n]*\\'" "" output)) ; trim string
245 with start = 0
246 with end = (length output)
247 for ret = (read-from-string output start end)
248 for data = (first ret)
249 do (setq start (rest ret))
250 do (parallel--process-output connection (first data) (rest data))
251 until (= start end)))
252 247
253(defun parallel--process-output (connection task result) 248(defun parallel--process-output (connection task result)
249 (put task 'connection connection)
254 (cond ((and (not (get task 'initialized)) 250 (cond ((and (not (get task 'initialized))
255 (eq result 'code)) 251 (eq result 'code))
256 (process-send-string connection 252 (apply #'parallel-send
257 (parallel--call-with-env (get task 'exec-fun) 253 task
258 (get task 'env))) 254 (get task 'exec-fun)
255 (get task 'env))
256 (let ((code nil))
257 (while (setq code (pop (get task 'queue)))
258 (apply #'parallel-send task (car code) (cdr code))))
259 (put task 'initialized t)) 259 (put task 'initialized t))
260 (t 260 (t
261 (push result (get task 'results)) 261 (push result (get task 'results))
@@ -296,6 +296,15 @@ result returned by exec-fun."
296 "Stop TASK." 296 "Stop TASK."
297 (delete-process (get task 'proc))) 297 (delete-process (get task 'proc)))
298 298
299(defun parallel-send (task fun &rest env)
300 "Send FUN to be evaluated by TASK in ENV."
301 (let ((connection (get task 'connection)))
302 (if connection
303 (process-send-string
304 connection
305 (parallel--call-with-env fun env))
306 (push (cons fun env) (get task 'queue)))))
307
299(provide 'parallel) 308(provide 'parallel)
300 309
301;;; parallel.el ends here 310;;; parallel.el ends here
diff --git a/lisp/xwidget.el b/lisp/xwidget.el
index c0d58972e14..1f0932ca7dd 100644
--- a/lisp/xwidget.el
+++ b/lisp/xwidget.el
@@ -59,12 +59,14 @@ see `make-xwidget' for types suitable for TYPE."
59;; ))))) 59;; )))))
60 60
61(defun xwidget-display (xwidget) 61(defun xwidget-display (xwidget)
62 "Force xwidget to be displayed to create a xwidget_view." 62 "Force xwidget to be displayed to create a xwidget_view. Return
63the window displaying XWIDGET."
63 (let* ((buffer (xwidget-buffer xwidget)) 64 (let* ((buffer (xwidget-buffer xwidget))
64 (window (display-buffer buffer)) 65 (window (display-buffer buffer))
65 (frame (window-frame window))) 66 (frame (window-frame window)))
66 (set-frame-visible frame t) 67 (set-frame-visible frame t)
67 (redisplay t))) 68 (redisplay t)
69 window))
68 70
69 71
70;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/test/automated/xwidget-tests.el b/test/automated/xwidget-tests.el
index 104daa3fc0f..7f79c9422f6 100644
--- a/test/automated/xwidget-tests.el
+++ b/test/automated/xwidget-tests.el
@@ -38,8 +38,8 @@
38 (let* ((buffer (current-buffer)) 38 (let* ((buffer (current-buffer))
39 (xwidget (make-xwidget beg end type title width height data buffer))) 39 (xwidget (make-xwidget beg end type title width height data buffer)))
40 (set-xwidget-query-on-exit-flag xwidget nil) 40 (set-xwidget-query-on-exit-flag xwidget nil)
41 (parallel-send (coerce (xwidget-info xwidget) 'list)) 41 (parallel-remote-send (coerce (xwidget-info xwidget) 'list))
42 (parallel-send (buffer-name buffer)) 42 (parallel-remote-send (buffer-name buffer))
43 (buffer-name (xwidget-buffer xwidget))))) 43 (buffer-name (xwidget-buffer xwidget)))))
44 :env (list beg end type title width height data))) 44 :env (list beg end type title width height data)))
45 (results (parallel-get-results proc))) 45 (results (parallel-get-results proc)))
@@ -57,7 +57,7 @@
57 (parallel-start (lambda () 57 (parallel-start (lambda ()
58 (require 'xwidget) 58 (require 'xwidget)
59 (let ((xwidget (make-xwidget 1 1 'Button "Button" 100 100 nil))) 59 (let ((xwidget (make-xwidget 1 1 'Button "Button" 100 100 nil)))
60 (parallel-send (xwidget-query-on-exit-flag xwidget)) 60 (parallel-remote-send (xwidget-query-on-exit-flag xwidget))
61 (set-xwidget-query-on-exit-flag xwidget nil) 61 (set-xwidget-query-on-exit-flag xwidget nil)
62 (xwidget-query-on-exit-flag xwidget)))))))) 62 (xwidget-query-on-exit-flag xwidget))))))))
63 63
@@ -90,11 +90,10 @@
90 (with-temp-buffer 90 (with-temp-buffer
91 (insert ?\0) 91 (insert ?\0)
92 (let* ((xwidget (xwidget-insert 1 type title 100 100)) 92 (let* ((xwidget (xwidget-insert 1 type title 100 100))
93 (window (display-buffer (current-buffer)))) 93 (window (xwidget-display xwidget)))
94 (set-xwidget-query-on-exit-flag xwidget nil) 94 (set-xwidget-query-on-exit-flag xwidget nil)
95 (set-frame-visible (window-frame window) t) 95 (xwidget-view-p
96 (redisplay t) 96 (xwidget-view-lookup xwidget window)))))
97 (xwidget-view-p (xwidget-view-lookup xwidget window)))))
98 :env (list type title) 97 :env (list type title)
99 :graphical t 98 :graphical t
100 :emacs-args '("-T" "emacs-debug"))))) 99 :emacs-args '("-T" "emacs-debug")))))