Emacs Berlin (virtual, in English) https://emacs-berlin.org/ Wed Feb 24 0930 Vancouver / 1130 Chicago / 1230 Toronto / 1730 GMT / 1830 Berlin / 2300 Kolkata // Thu Feb 25 0130 Singapore
Emacs APAC (virtual, in English) https://emacs-apac.gitlab.io/ Sat Feb 27 0030 Vancouver / 0230 Chicago / 0330 Toronto / 0830 GMT / 0930 Berlin / 1400 Kolkata / 1630 Singapore
EmacsFFM: Creating our own major mode (virtual) https://www.meetup.com/emacs-ffm/events/275881281/ Wed Feb 17 1100 Vancouver / 1300 Chicago / 1400 Toronto / 1900 GMT / 2000 Berlin // Thu Feb 18 0030 Kolkata / 0300 Singapore
Emacs Berlin (virtual, in English) https://emacs-berlin.org/ Wed Feb 24 0930 Vancouver / 1130 Chicago / 1230 Toronto / 1730 GMT / 1830 Berlin / 2300 Kolkata // Thu Feb 25 0130 Singapore
EmacsSF: Discussion of emacs in general and completion frameworks in particular https://www.meetup.com/Emacs-SF/events/276234974/ Fri Feb 12 1000 Vancouver / 1200 Chicago / 1300 Toronto / 1800 GMT / 1900 Berlin / 2330 Kolkata // Sat Feb 13 0200 Singapore
EmacsFFM: Creating our own major mode (virtual) https://www.meetup.com/emacs-ffm/events/275881281/ Wed Feb 17 1100 Vancouver / 1300 Chicago / 1400 Toronto / 1900 GMT / 2000 Berlin // Thu Feb 18 0030 Kolkata / 0300 Singapore
Emacs Berlin (virtual, in English) https://emacs-berlin.org/ Wed Feb 24 0930 Vancouver / 1130 Chicago / 1230 Toronto / 1730 GMT / 1830 Berlin / 2300 Kolkata // Thu Feb 25 0130 Singapore
Emacs APAC (virtual, in English) https://emacs-apac.gitlab.io/ Sat Feb 27 0030 Vancouver / 0230 Chicago / 0330 Toronto / 0830 GMT / 0930 Berlin / 1400 Kolkata / 1630 Singapore
I read a lot of PDF documents on Emacs. I use the Emacs package pdf-tools for this. I also write a lot of LaTeX on Emacs (both in org-mode notes and .tex files). Frequently I find myself in the following situation: I need to jump between sections in documents to refer to definitions, before I resume my reading where I was. The natural Emacs keybindings for this are C-x r SPACE r (point-to-register) and C-x r j r (jump-to-register), which I found by searching online. I could not memorize them, so I decided to write my own keybindings that do the task. I read up on the documentation of these functions using C-h f, describing their function names and looking at the docstring. I realized that they receive an argument which is the “register name” and Emacs does the magic thing, the following two elisp lines ended up in my .emacs (after going through the usual phase where I look up online “how to set a keybinding on Emacs”, because I never remember these things):
;; Make C-f1 and C-f2 save at point & jump to region;; useful when going back-and-forth between definitions in a file;; subsequent code is to make this work for pdf-tools as well.
(global-set-key (kbd "<C-f1>") (lambda () (interactive) (point-to-register ?r)))
(global-set-key (kbd "<C-f2>") (lambda () (interactive) (jump-to-register ?r)))
I tested it with some text files and it worked wonderfully. However, it did not work on pdf-tools! There’s something special about the way that package treats files that is not compatible with Emacs registers. I looked around online for solutions, and found this github issue. I skimmed through with my usual impatience and ignorant attitude that only cares for the solution. Someone there mentioned the package saveplace-pdf-view, which I looked up on github. In its source code, I was able to locate the functions pdf-view-bookmark-make-record and pdf-view-bookmark-jump, which (thank goodness!) do what I need. I obtained this:
(define-key pdf-view-mode-map (kbd "<C-f1>")
(lambda ()
"Saves the current position on the pdf to jump to later with <C-f2>."
(interactive)
(setf my-bookmark (pdf-view-bookmark-make-record))))
(define-key pdf-view-mode-map (kbd "<C-f2>")
(lambda ()
"Loads the position saved by <C-f1>."
(interactive)
(pdf-view-bookmark-jump my-bookmark)))
It works wonderfully, just like the text case, thanks to the folks who designed these functions (they remember the exact position, not just page). However, they only allow me to save one point at a time. It would be nice to be able to save multiple points, so that I can jump around various places on the document, definitions I often visit, other places of interest. So what I needed was a way to save in a variable all these bookmarks generated by pdf-view-bookmark-make-record, and then be able to look them up using a name. Anticipating all this, I wrote the following:
(defvarmy-bookmarks nil
"List of bookmarks, useful for pdf-mode where I save my positions with <C-f1> etc.")
(defconstmy-default-bookmark ?1
"This is the default bookmark name")
(define-key pdf-view-mode-map (kbd "<C-f1>")
(lambda ()
"Saves the current position on the pdf to jump to later with <C-f2>."
(interactive)
(setf (alist-get my-default-bookmark my-bookmarks) (pdf-view-bookmark-make-record))))
(define-key pdf-view-mode-map (kbd "<C-f2>")
(lambda ()
"Loads the position saved by <C-f1>."
(interactive)
(pdf-view-bookmark-jump (alist-get my-default-bookmark my-bookmarks))))
That’s a typical use of an associative list, a data structure that Emacs is fond of. I looked up online ways to handle associative lists (or alists) in elisp. I wanted to emulate the behavior of point-to-register, which requires the register name (as a single character). Incidentally, single characters in elisp are denoted by ?a, ?b, ?c, etc. I found a nice Stack Exchange post that explained how to set and get values from alists. It works like this:
;; ↓This is the key↓ ↓This is the alist↓
(setf (alist-get my-bookmark-name my-bookmarks)
my-value) ;; ← This is the value (bookmark data)
If you are not familiar with setf, it asks for a place to store something to, and alist-get points to the particular entry of your alist that you ask for.
The last ingredient that I needed was to figure out a way to emulate the behavior of C-x r SPACE, which shows in the minibuffer “Point to register:” and waits for input. I tried reading the documentation of point-to-register to understand how this is done, but I couldn’t figure it out. I had a vague idea it is possible with (interactive) so I looked up some examples. It wasn’t easy to figure out, but at the end I realized that the “Code characters for interactive” are just some prefix codes in the string provided to interactive that tell it how to supply its caller with input from Emacs.
Pressing <C-f5> and entering "a", sure enough prints "Read: a", which is great. All I need to do is ask for the bookmark name to save to or load from. I decided to use the neighboring keybindings <C-f3> and <C-f4> for those purposes, still retaining <C-f1> and <C-f2> for a quick lookup.
I was ready to complete the functionality for pdf-tools,
(defvarmy-bookmarks nil
"List of bookmarks, useful for pdf-mode where I save my positions with <C-f1> etc.")
(defconstmy-default-bookmark ?1
"This is the default bookmark name")
(defunmy-save-pdf-position (&optional b)
"Saves the current PDF position of pdf-tools at a bookmark named B."
(unless b (setq b my-default-bookmark))
(setf (alist-get b my-bookmarks)
(pdf-view-bookmark-make-record)))
(defunmy-load-pdf-position (&optional b)
"Loads the PDF position saved at the bookmark named B."
(unless b (setq b my-default-bookmark))
(pdf-view-bookmark-jump (alist-get b my-bookmarks)))
(define-key pdf-view-mode-map (kbd "<C-f1>")
(lambda ()
(interactive)
(my-save-pdf-position)))
(define-key pdf-view-mode-map (kbd "<C-f2>")
(lambda ()
(interactive)
(my-load-pdf-position)))
(define-key pdf-view-mode-map (kbd "<C-f3>")
(lambda (b) (interactive"cSaving to bookmark name (single character): ")
(my-save-pdf-position b)))
(define-key pdf-view-mode-map (kbd "<C-f4>")
(lambda (b) (interactive"cLoading from bookmark name (single character): ")
(my-load-pdf-position b)))
Now I could just complement it with the text functionality
I also have to thank the folks over at freenode’s #emacs channel for helping me with this (and many other things over the years), and Sacha Chua in particular for encouraging me to write this blog post. So, big thank you!
The whole code, together with the comments, is below
;; Make <C-f1> and <C-f2> save at point & jump to region.;; Useful when going back-and-forth between definitions in a file.;; The code below makes this work for pdf-tools as well.;;;; You can use <C-f3> and <C-f4> to have more save and load slots.;; They are named by single characters, i.e. try;; <C-f3> 5;; to save to slot 5 (you can use a letter as well);; <C-f4> 5;; to load from slot 5. The default slot name is 1.
(defvarmy-bookmarks nil
"List of bookmarks, useful for pdf-mode where I save my positions with <C-f1> etc.")
(defconstmy-default-bookmark ?1
"This is the default bookmark name")
(defunmy-save-pdf-position (&optional b)
"Saves the current PDF position of pdf-tools at a bookmark named B."
(unless b (setq b my-default-bookmark))
(setf (alist-get b my-bookmarks)
(pdf-view-bookmark-make-record)))
(defunmy-load-pdf-position (&optional b)
"Loads the PDF position saved at the bookmark named B."
(unless b (setq b my-default-bookmark))
(pdf-view-bookmark-jump (alist-get b my-bookmarks)))
(define-key pdf-view-mode-map (kbd "<C-f1>")
(lambda ()
(interactive)
(my-save-pdf-position)))
(define-key pdf-view-mode-map (kbd "<C-f2>")
(lambda ()
(interactive)
(my-load-pdf-position)))
(define-key pdf-view-mode-map (kbd "<C-f3>")
(lambda (b) (interactive"cSaving to bookmark name (single character): ")
(my-save-pdf-position b)))
(define-key pdf-view-mode-map (kbd "<C-f4>")
(lambda (b) (interactive"cLoading from bookmark name (single character): ")
(my-load-pdf-position b)))
(global-set-key (kbd "<C-f1>") (lambda () (interactive) (point-to-register my-default-bookmark)))
(global-set-key (kbd "<C-f2>") (lambda () (interactive) (jump-to-register my-default-bookmark)))
(global-set-key (kbd "<C-f3>") (lambda (r) (interactive"cSaving to register: ") (point-to-register r)))
(global-set-key (kbd "<C-f4>") (lambda (r) (interactive"cLoading from register: ") (jump-to-register r)))
-–— Note from Sacha:
You can bind interactive functions without using a lambda, like this:
You can distinguish multiple uses of the prefix argument by checking the value of current-prefix-arg.
Alternatively, sometimes people define an interactive function that doesn’t have arguments, and they have that function call the other interactive function that has arguments.
Also, if you’re trying the keyboard shortcuts in this and you’re wondering why Ctrl F1 doesn’t seem to work for you (like it didn’t work for me), check if your window manager is using the shortcuts for something else. I’m on KDE, so I needed to use Global Shortcuts to remove the KWin keyboard shortcuts for changing to desktop 1, 2, 3, and 4.
Let me know if you have comments or feedback and I can pass them along. If you want to share a tip about Emacs and don’t have a place to put it, feel free to send it to me too. Enjoy!
Update: 2021-02-11: Parsed pacmd list-sources so that I can mute/unmute devices by regular expression. Update: 2021-02-07: Made it work with my USB microphone.
I was experimenting with streaming Emacs geeking around on twitch.tv. Someone asked me to have soft background music and typing sounds. Since I’m a little clueless about music and don’t want to bother with hunting down nice royalty-free music, I figured I could just use the Mozart dice game to programmatically generate music.
I installed the mozart-dice-game NPM package and used this bit of Javascript to generate a hundred MIDI files.
const x = require('mozart-dice-game')
for (leti = 0; i < 100; i++) { x.saveMinuet('minuet' + String(i).padStart('3', '0') + '.mid'); }
Then I wrote this Emacs Lisp function to turn it on and off.
People also suggested typing sounds. I guess that’s a good way to get a sense of activity. The default selectric sound was a little too loud for me, so we’ll use the move sound for now. It would be nice to make this more random-sounding someday.
(defunmy/selectric-type-sound ()
"Make the sound of typing.";; Someday, randomize this or something
(selectric-make-sound (expand-file-name "selectric-move.wav" selectric-files-path)))
(use-packageselectric-mode:diminish"":config
(fset #'selectric-type-sound #'my/selectric-type-sound))
I was having a hard time remembering to go back on mute during meetings, since the LED on the mute button wasn’t working at the time and the system tray icon was a little hard to notice. The LED has mysteriously decided to start working again, but push-to-talk is handy anyway. I want to be able to tap a key to toggle my microphone on and off, and hold it down in order to make it push-to-talk. It looks like my key repeat is less than 0.5 seconds, so I can set a timer that will turn things off after a little while. This code doesn’t pick up any changes that happen outside Emacs, but it’ll do for now. I used pacmd list-sources to list the sources and get the IDs.
(defunmy/pacmd-set-device (regexp status)
(with-current-buffer (get-buffer-create "*pacmd*")
(erase-buffer)
(shell-command "pacmd list-sources" (current-buffer))
(goto-char (point-max))
(let (results)
(while (re-search-backward regexp nil t)
(when (re-search-backward "index: \\([[:digit:]]+\\)" nil t)
(setq results (cons (match-string 1) results))
(shell-command-to-string (format "pacmd set-source-mute %s %d"
(match-string 1)
(if (equal status 'on) 0 1)))))
results)))
(defvarmy/mic-p nil "Non-nil means microphone is on")
(add-to-list 'mode-line-front-space '(:eval (if my/mic-p "*MIC*""")))
(defunmy/mic-off ()
(interactive)
(my/pacmd-set-device "Yeti" 'off)
(my/pacmd-set-device "Internal Microphone" 'off)
(setq my/mic-p nil))
(defunmy/mic-on ()
(interactive)
(my/pacmd-set-device "Yeti" 'on)
(my/pacmd-set-device "Internal Microphone" 'on)
(setq my/mic-p t))
(defunmy/mic-toggle ()
(interactive)
(if my/mic-p (my/mic-off) (my/mic-on)))
(defvarmy/push-to-talk-mute-timer nil "Timer to mute things again.")
(defvarmy/push-to-talk-last-time nil "Last time my/push-to-talk was run")
(defvarmy/push-to-talk-threshold 0.5 "Number of seconds")
(defunmy/push-to-talk-mute ()
(interactive)
(message "Muting.")
(my/mic-off)
(force-mode-line-update)
(my/obs-websocket-add-subtitle (my/obs-websocket-stream-time-msecs) "[Microphone off]"))
(defunmy/push-to-talk ()
"Tap to toggle microphone on and off, or repeat the command to make it push to talk."
(interactive)
(cond
((null my/mic-p) ;; It's off, so turn it on
(when (timerp my/push-to-talk-mute-timer)
(cancel-timer my/push-to-talk-mute-timer))
(my/mic-on)
(my/obs-websocket-add-subtitle (my/obs-websocket-stream-time-msecs) "[Microphone on]")
(setq my/push-to-talk-last-time (current-time)))
((timerp my/push-to-talk-mute-timer) ;; Push-to-talk mode
(cancel-timer my/push-to-talk-mute-timer)
(setq my/push-to-talk-mute-timer
(run-at-time my/push-to-talk-threshold nil #'my/push-to-talk-mute)))
;; Might be push to talk, if we're within the key repeating time
((< (- (time-to-seconds (current-time)) (time-to-seconds my/push-to-talk-last-time))
my/push-to-talk-threshold)
(setq my/push-to-talk-mute-timer
(run-at-time my/push-to-talk-threshold nil #'my/push-to-talk-mute)))
;; It's been a while since I turned the mic on.
(t (my/push-to-talk-mute))))
(global-set-key (kbd "<f12>") #'my/push-to-talk)
300+ sketches about learning, sharing, tech, semi-retirement, business, delegation, and other topics. You'll get a ZIP file (290+ MB) with hi-res images that you can easily flip through or print.
What's getting in your way when it comes to blogging consistently? Check out this 42-page guide to ten common blogging excuses and how to break past them.
林晃 Hi Sacha, thank you for hosting great blog! How do you gather such large amount of information?!
Wonderment The following table was taken from a paper by D.R. Dapp et all. 2016. Respiratory mode and gear type are important determinants of elasmobranch immediate...
Wonderment http://uploads.disquscdn.com/images/08a87c15c49e6b0dff513aac84986b9af6937145cfc773e6f7a172fc4d9adac8.jpg I found a table.
Greg Coladonato It helped! Maraming Salamat Sacha! See you on the Emacs SF call again some time :)
Wonderment After some Google Scholaring: this is a deeper question than I had imagined. Sharks with buccal ventilation, such as you suggest, are more apt to...