Browse Source

Initial commit

master
Peter J. Jones 1 year ago
commit
49177f23c2
Signed by: Peter Jones <pjones@devalot.com> GPG Key ID: 9DAFAA8D01941E49
4 changed files with 333 additions and 0 deletions
  1. 0
    0
      CHANGES.md
  2. 20
    0
      LICENSE
  3. 0
    0
      README.md
  4. 313
    0
      exwm-browsers.el

+ 0
- 0
CHANGES.md View File


+ 20
- 0
LICENSE View File

@@ -0,0 +1,20 @@
1
+Copyright (C) 2018 Peter Jones <pjones@devalot.com>
2
+
3
+Permission is hereby granted, free of charge, to any person obtaining
4
+a copy of this software and associated documentation files (the
5
+"Software"), to deal in the Software without restriction, including
6
+without limitation the rights to use, copy, modify, merge, publish,
7
+distribute, sublicense, and/or sell copies of the Software, and to
8
+permit persons to whom the Software is furnished to do so, subject to
9
+the following conditions:
10
+
11
+The above copyright notice and this permission notice shall be
12
+included in all copies or substantial portions of the Software.
13
+
14
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 0
- 0
README.md View File


+ 313
- 0
exwm-browsers.el View File

@@ -0,0 +1,313 @@
1
+;;; exwm-browsers.el --- Control web browsers running under EXWM. -*- lexical-binding: t -*-
2
+
3
+;; Copyright (C) 2018 Peter Jones <pjones@devalot.com>
4
+
5
+;; Author: Peter Jones <pjones@devalot.com>
6
+;; Homepage: https://github.com/pjones/exwm-browsers
7
+;; Package-Requires: ((emacs "24.4") (async "1.9") (exwm "0.18"))
8
+;; Version: 0.1.0
9
+;;
10
+;; This file is not part of GNU Emacs.
11
+
12
+;;; Commentary:
13
+;;
14
+;; FIXME:
15
+
16
+;;; License:
17
+;;
18
+;; Permission is hereby granted, free of charge, to any person obtaining
19
+;; a copy of this software and associated documentation files (the
20
+;; "Software"), to deal in the Software without restriction, including
21
+;; without limitation the rights to use, copy, modify, merge, publish,
22
+;; distribute, sublicense, and/or sell copies of the Software, and to
23
+;; permit persons to whom the Software is furnished to do so, subject to
24
+;; the following conditions:
25
+;;
26
+;; The above copyright notice and this permission notice shall be
27
+;; included in all copies or substantial portions of the Software.
28
+;;
29
+;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30
+;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31
+;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32
+;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33
+;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34
+;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35
+;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36
+
37
+;;; Code:
38
+(require 'async)
39
+(require 'browse-url)
40
+(require 'seq)
41
+(require 'exwm)
42
+
43
+
44
+;;; Classes
45
+;;
46
+;;
47
+(defclass exwm-browser ()
48
+  ((name
49
+    :initarg :name
50
+    :initform nil
51
+    :custom string
52
+    :documentation
53
+    "The name of the browser shown in prompts and menus.")
54
+
55
+   (class-name
56
+    :initarg :class-name
57
+    :initform nil
58
+    :custom string
59
+    :documentation
60
+    "  The class name property of the browser window exposed by the
61
+  `exwm-class-name' variable.  Used to detect if the current
62
+  window is a browser and which type of browser it is.")
63
+
64
+   (open-url
65
+    :initarg :open-url
66
+    :initform nil
67
+    :custom function
68
+    :documentation
69
+    "  A function returning a list that can be passed to
70
+  `asyn-start-process' to start a new instance/tab of the browser.  It
71
+  should take a single argument, the URL to open.")
72
+
73
+   (get-url
74
+    :initarg :get-url
75
+    :initform nil
76
+    :custom function
77
+    :documentation
78
+    "  A function returning the URL being displayed in a browser
79
+  window.  The EXWM buffer holding the browser will be current when
80
+  this function is called.")
81
+
82
+   (set-url
83
+    :initarg :set-url
84
+    :initform nil
85
+    :custom function
86
+    :documentation
87
+    "  A function that sets the URL of the browser.  Its argument will
88
+  be the new URL to set in the browser window.  The EXWM buffer
89
+  holding the browser window will be current when this function is
90
+  called.")
91
+
92
+   (history-back
93
+    :initarg :history-back
94
+    :initform nil
95
+    :custom function
96
+    :documentation
97
+    "  A function to move backwards in the location history.")
98
+
99
+   (history-forward
100
+    :initarg :history-forward
101
+    :initform nil
102
+    :custom function
103
+    :documentation
104
+    "  A function to move forwards in the location history.")))
105
+
106
+(defmethod exwm-browser--open-url ((browser exwm-browser) url new-window)
107
+  "Open URL in BROWSER.
108
+
109
+If NEW-WINDOW is nil then use the set-url function slot to do
110
+this.  Otherwise, when NEW-WINDOW is non-nil, use the open-url
111
+function slot."
112
+  (if new-window
113
+      (let* ((func (oref browser open-url))
114
+             (args (and func (apply func url nil))))
115
+        (when args (apply #'async-start-process
116
+                          (car args) (car args) nil (cdr args))))
117
+    (let ((func (oref browser set-url)))
118
+      (when func (apply func url nil)))))
119
+
120
+
121
+;;; Predefined browsers:
122
+(defconst exwm-browsers-surf
123
+  (exwm-browser :name "Surf"
124
+                :class-name "Surf"
125
+                :open-url (lambda (url) (list "surf" url))
126
+                :get-url  (lambda () (exwm-browsers-get-prop "_SURF_URI"))
127
+                :set-url  (lambda (url) (exwm-browsers-set-prop "_SURF_GO" url))
128
+                :history-back (lambda () (exwm-browsers-send-keys exwm-browsers-surf-back-key))
129
+                :history-forward (lambda () (exwm-browsers-send-keys exwm-browsers-surf-forward-key)))
130
+  "The Surf web browser.")
131
+
132
+
133
+;;; Customize interface:
134
+(defgroup exwm-browsers nil
135
+  "A minor mode and helpers for managing web browsers under EXWM."
136
+  :version "0.1.0"
137
+  :prefix "exwm-browsers-"
138
+  :group 'applications)
139
+
140
+(defcustom exwm-browsers-list
141
+  (list exwm-browsers-surf)
142
+  "List of supported browsers.")
143
+
144
+(defcustom exwm-browsers-surf-back-key [?\C-b]
145
+  "The key to send to Surf to make it move back in its history."
146
+  :group 'exwm-browsers
147
+  :type 'key-sequence)
148
+
149
+(defcustom exwm-browsers-surf-forward-key [?\C-f]
150
+  "The key to send to Surf to make it move forward in its history."
151
+  :group 'exwm-browsers
152
+  :type 'key-sequence)
153
+
154
+(defcustom exwm-browsers-prompt-for-browser nil
155
+  "Whether opening a URL should prompt for which browser to use.
156
+
157
+Setting this to non-nil means always prompt.  The default setting
158
+is to use the first configured browser instead of prompting.
159
+
160
+See `exwm-browsers-list' for the configured browser list."
161
+  :group 'exwm-browsers
162
+  :type '(repeat FIXME:))
163
+
164
+(defcustom exwm-browsers-shortcuts
165
+  '("https://duckduckgo.com/?q=%s"
166
+    "https://www.google.com/search?q=%s"
167
+    "https://en.wikipedia.org/w/index.php?title=Special:Search&search=%s&go=Go")
168
+  "URLs of frequently visited sites and search engines.
169
+
170
+This list is used by the `exwm-browsers-open-shortcut' command to
171
+build an interactive prompt for selecting a URL and jumping to it
172
+in a new browser window/tab.
173
+
174
+A URL can optionally include a %s format specifier.  If such a
175
+URL is selected from the interactive prompt a second prompt will
176
+be show to read a search string.  In this mode the %s in the URL
177
+will be substituted for the input from the second prompt
178
+before launching a browser."
179
+  :group 'exwm-browsers
180
+  :type '(repeat (choice string)))
181
+
182
+
183
+;;; Internal variables:
184
+(defvar exwm-browser-mode-map
185
+  (let ((map (make-sparse-keymap)))
186
+    (define-key map (kbd "C-c C-f") #'exwm-browsers-history-forward)
187
+    (define-key map (kbd "C-c C-b") #'exwm-browsers-history-back)
188
+    (define-key map (kbd "C-c C-l") #'exwm-browsers-open-url)
189
+    (define-key map (kbd "C-c C-j") #'exwm-browsers-open-shortcut)
190
+    map)
191
+  "Key map for `exwm-browser-mode'.")
192
+
193
+
194
+;;; Functions:
195
+(defun exwm-browsers-winid ()
196
+  "Get the X11 window ID for the current EXWM buffer."
197
+  (if (derived-mode-p 'exwm-mode)
198
+      (exwm--buffer->id (current-buffer))
199
+    (error "Not a EXWM window")))
200
+
201
+(defun exwm-browsers-get-prop (name)
202
+  "Read window property NAME from the current EXWM buffer."
203
+  ;; FIXME: This is a hack stolen from exwm-surf: Copyright (C) 2017
204
+  ;; craven@gmx.net without permission.
205
+  (let* ((winid (exwm-browsers-winid))
206
+         (text (shell-command-to-string
207
+                (format "xprop -notype -id %s %s" winid name))))
208
+    (string-match "\"\\(.*\\)\"" text)
209
+    (match-string 1 text)))
210
+
211
+(defun exwm-browsers-set-prop (name value)
212
+  "Set the X11 window property NAME to VALUE for the current EXWM buffer."
213
+  (let ((winid (exwm-browsers-winid)))
214
+    (start-process-shell-command
215
+     "xprop" nil (format "xprop -id %s -f %s 8s -set %s \"%s\""
216
+                         winid name name value))))
217
+
218
+(defun exwm-browsers-send-keys (keys)
219
+  "Send the key sequence KEYS to the current EXWM window."
220
+  (when (derived-mode-p 'exwm-mode)
221
+    (seq-doseq (key keys)
222
+      (exwm-input--fake-key key))))
223
+
224
+(defun exwm-browsers-choose-browser ()
225
+  "Prompt the user to select a browser."
226
+  ;; FIXME:
227
+  exwm-browsers-surf)
228
+
229
+(defun exwm-browsers-current-browser ()
230
+  "Return the browser instance for the current window or nil."
231
+  (when (derived-mode-p 'exwm-mode)
232
+    (object-assoc exwm-class-name 'class-name exwm-browsers-list)))
233
+
234
+(defun exwm-browsers-funcall (slot arguments)
235
+  "Call a function at SLOT with ARGUMENTS for the current browser buffer."
236
+  (let* ((browser (exwm-browsers-current-browser))
237
+         (func (and browser (slot-value browser slot))))
238
+    (when func (apply func arguments))))
239
+
240
+(defun exwm-browsers-url-prompt ()
241
+  "Prompt for a URL.
242
+
243
+If the current window is a browser, extract a URL from it and use
244
+it as the default answer in the prompt.  Otherwise act like
245
+`browse-url-interactive-arg' and try to find a URL around point
246
+to use as the default answer.
247
+
248
+Return a list just like `browse-url-interactive-arg' does where
249
+the CAR is the URL read from the user and the CADR is the
250
+new-window flag."
251
+  (let ((prompt "URL: ")
252
+        (url (exwm-browsers-funcall 'get-url nil)))
253
+    (if url (list (read-string prompt url)
254
+                  (not (eq (null browse-url-new-window-flag)
255
+                           (null current-prefix-arg))))
256
+      (browse-url-interactive-arg prompt))))
257
+
258
+
259
+;;; Commands:
260
+(defun exwm-browsers-open-url (url &optional new-window)
261
+  "Open URL in a web browser.
262
+
263
+If the current buffer is already a browser window and NEW-WINDOW
264
+is nil, redirect the browser to URL replacing the current
265
+location.  Otherwise, when NEW-WINDOW is non-nil, always open a
266
+new browser window/tab."
267
+  (interactive (exwm-browsers-url-prompt))
268
+  (let ((browser (or (and (not new-window) (exwm-browsers-current-browser))
269
+                     (exwm-browsers-choose-browser)
270
+                     exwm-browsers-surf)))
271
+    (exwm-browser--open-url browser url
272
+                            (or new-window
273
+                                (not (exwm-browsers-current-browser))))))
274
+
275
+(defun exwm-browsers-open-shortcut (shortcut &optional new-window)
276
+  "Open a shortcut URL in a browser window.
277
+
278
+When called interactively prompt for SHORTCUT.  If SHORTCUT
279
+contains a %s format placeholder also prompt for a search query.
280
+
281
+If NEW-WINDOW is non-nil then display the URL in a new window."
282
+  (interactive
283
+   (list (completing-read "Shortcut: " exwm-browsers-shortcuts)
284
+         current-prefix-arg))
285
+  (let ((url (if (string-match-p "%s" shortcut)
286
+                 (format shortcut (read-string "Search Query: "))
287
+               shortcut)))
288
+    (exwm-browsers-open-url url new-window)))
289
+
290
+(defun exwm-browsers-history-back ()
291
+  "Tell the current browser to go to the previous URL in the history."
292
+  (interactive)
293
+  (exwm-browsers-funcall 'history-back nil))
294
+
295
+(defun exwm-browsers-history-forward ()
296
+  "Tell the current browser to go to the next URL in the history."
297
+  (interactive)
298
+  (exwm-browsers-funcall 'history-forward nil))
299
+
300
+(define-minor-mode exwm-browser-mode
301
+  "Minor mode to interact with a web browser displayed by EXWM."
302
+  :group  'exwm-browsers
303
+  :keymap 'exwm-browser-mode-map)
304
+
305
+(defun exwm-browser-mode-maybe-enable ()
306
+  "Activate `exwm-browser-mode' if the current buffer is a browser."
307
+  (if (exwm-browsers-current-browser) (exwm-browser-mode 1)))
308
+
309
+;; Try to automatically enable `exwm-browser-mode' when needed:
310
+(add-hook 'exwm-manage-finish-hook #'exwm-browser-mode-maybe-enable)
311
+
312
+(provide 'exwm-browsers)
313
+;;; exwm-browsers.el ends here

Loading…
Cancel
Save