Skip to content

Instantly share code, notes, and snippets.

@QiangF
Last active January 2, 2023 03:10
Show Gist options
  • Save QiangF/f3ca62cc24b5b5792393eeead07591d7 to your computer and use it in GitHub Desktop.
Save QiangF/f3ca62cc24b5b5792393eeead07591d7 to your computer and use it in GitHub Desktop.
rofi in elisp
emacsclient -c -F '((auto-raise . t) (visibility . t) (sticky . nil) (skip-taskbar . t) (undecorated . t) (name . "erofi") (width . 120) (height . 20) (vertical-scroll-bars . nil) (minibuffer . only))' -e "(progn (x-focus-frame nil) (rofi-in-elisp \"$1\"))"
;;; rofi-in-elisp.el --- An Elisp implementation of Rofi -*- lexical-binding: t -*-
(defvar rofi-in-elisp-web-history-file
"~/.config/rofi-in-elisp/history"
"File to write history for web searches.")
(defvar rofi-in-elisp-web-provider-default "metager"
"Default search provider for `rofi-in-elisp' (web modi).")
(defvar rofi-in-elisp-web-url-alist
'(("duckduckgo" . "https://www.duckduckgo.com/?q=")
("youtube" . "https://www.youtube.com/results?search_query="))
"Alist mapping providers to search URLs, to be used by
`rofi-in-elisp' function's 'web' search modi.")
(require 'cl-lib)
(require 'ivy)
(defun ewmctrl-list-windows ()
"Internal function to get a list of desktop windows via `wmctrl'."
(let* ((bfr (generate-new-buffer " *ewmctrl-output*"))
(current-desktop (car (last (split-string (shell-command-to-string "xprop -root -notype _NET_CURRENT_DESKTOP")))))
(wmctrl-path "/usr/bin/wmctrl")
(wmctrl-field-count 6)
(fields-re
(concat "^" (mapconcat 'identity (make-list (1- wmctrl-field-count) "\\(\\S-+\\)\\s-+") "") "\\(.+\\)"))
(win-id-list '())
(win-class-list '())
(win-class-width 5)
(win-title-list '())
win-list
current-win
win-num)
(call-process-shell-command (concat wmctrl-path " " "-lpx") nil bfr)
(with-current-buffer bfr
(goto-char (point-min))
(while (re-search-forward fields-re nil t)
(let ((win-id (match-string 1))
(desktop-number (match-string 2))
(win-title (match-string wmctrl-field-count))
(win-class (car (last (split-string (match-string 4) "\\.")))))
(when (string-equal desktop-number current-desktop)
(setq win-class-width (max win-class-width (length win-class)))
(push win-id win-id-list)
(push win-class win-class-list)
(push win-title win-title-list)))))
(setq win-id-list (reverse win-id-list))
(setq win-class-list (reverse win-class-list))
(setq win-title-list (reverse win-title-list))
(setq win-num (length win-id-list))
(cl-loop for i from 1 to win-num do
(push (format (concat "%" (number-to-string (length (number-to-string win-num))) "s" ": "
"%-" (number-to-string win-class-width) "s" " "
"%s")
i (nth (1- i) win-class-list) (nth (1- i) win-title-list))
win-list))
(kill-buffer bfr)
(setq win-list (reverse win-list))
(setq current-win (pop win-list))
(list win-id-list
(append win-list (list current-win)))))
;;;###autoload
(defun rofi-in-elisp (modi)
"Main function, but you should not be calling this from Emacs.
See README for setup instructions."
(let ((mini-modeline-frame (next-frame))
(ivy-truncate-lines t)
(inhibit-message t))
(cond
;; Note: The "notify" modi was mostly for testing / playing
;; around. If you want to use it, you will need to (require
;; 'notify) somewhere, as the function `notify' (below)
;; depends on it.
((string= modi "notify")
(let* ((prompt (concat "Enter a message (" modi "): "))
(my-msg (concat "You entered: "
(completing-read prompt
'("a" "b" "c")))))
(notify "rofi-in-elisp" my-msg)))
((string= modi "window")
(let* ((ivy-height (- (frame-parameter nil 'height) 1))
(win-data (ewmctrl-list-windows))
(win-id-list (car win-data))
(selected-id (string-to-number (car (split-string (ivy-read "Choose window: " (cadr win-data)) ":")))))
(call-process "wmctrl" nil 0 nil "-ia" (nth (1- selected-id) win-id-list))))
((string= modi "clipboard")
(let* ((ivy-height (- (frame-parameter nil 'height) 1))
;; (app-info (emacs-everywhere-app-info))
(selected-string (ivy-read "Choose string: "
(split-string (shell-command-to-string "CM_LAUNCHER=rofi-script clipmenu") "\n" t))))
(call-process "emacs_clip" nil 0 nil selected-string)
(sleep-for 0.1)
(call-process "xdotool" nil 0 nil "key" "--clearmodifiers" "Shift+Insert")))
((string= modi "web")
(let* ((ivy-height (- (frame-parameter nil 'height) 1))
(provider (ivy-read
"Choose provider: "
rofi-in-elisp-web-url-alist
:preselect rofi-in-elisp-web-provider-default))
(provider_url (cdr
(assoc provider rofi-in-elisp-web-url-alist)))
(history (mapcar (lambda (row)
(nth 2 row))
(reverse (ostrta-file-data-to-list
rofi-in-elisp-web-history-file))))
;; We want to push the X CLIPBOARD and PRIMARY
;; selections both onto the top of the list for
;; convenience.
(history-and-clipboard
(progn (let ((clipboard (gui-get-selection 'CLIPBOARD))
(primary (gui-get-selection 'PRIMARY))
(h-a-c history))
(unless (null clipboard)
(push clipboard h-a-c))
(unless (null primary)
(push primary h-a-c))
h-a-c)))
(query (ivy-read
"Query: "
history-and-clipboard))
(query-url (concat provider_url query))
(timestamp (format-time-string "%F %T"))
(history-line (concat timestamp "" provider "" query "\n")))
(call-process "xdg-open" nil 0 nil query-url)
(with-current-buffer (find-file-noselect
rofi-in-elisp-web-history-file)
(goto-char (point-max))
(insert history-line)
(save-buffer))))))
(delete-frame))
(provide 'rofi-in-elisp)
;;; rofi-in-elisp.el ends here
1. start emacs server, load rofi-in-elisp:
(server-start) (require 'rofi-in-elisp)
2. bind "erofi.sh window" to a hot key, for example with openbox, in rc.xml:
<keybind key="A-Tab">
<action name="Execute">
<name>Rofi window switcher</name>
<command>erofi.sh window</command>
</action>
</keybind>
3. show desktop, (i.e. minimize all other windows and click on desktop, not taskbar) press the hotkey in 2, the emacsclient frame is hidden
4. with any window shown, press the hotkey in 2, the emacsclient frame pops up
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment