Skip to content

Commit

Permalink
Use wmctrl to re-pop-up frames which are only visible on other virtua…
Browse files Browse the repository at this point in the history
…l desktops

Fixes #2
  • Loading branch information
davidshepherd7 committed Jan 29, 2017
1 parent eb5bdb7 commit cbce743
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 9 deletions.
1 change: 1 addition & 0 deletions Cask
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

(development
(depends-on "ert-runner")
(depends-on "el-mock")
(depends-on "f")
(depends-on "ecukes")
(depends-on "espuds")
Expand Down
68 changes: 66 additions & 2 deletions frames-only-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

;; Author: David Shepherd <[email protected]>
;; Version: 1.0.0
;; Package-Requires: ((emacs "24.4") (dash "2.13.0"))
;; Package-Requires: ((emacs "24.4") (dash "2.13.0") (s "1.11.0"))
;; Keywords: frames, windows
;; URL: https://github.com/davidshepherd7/frames-only-mode

Expand All @@ -14,6 +14,7 @@
;;; Code:

(require 'dash)
(require 's)



Expand Down Expand Up @@ -99,6 +100,20 @@ mode please open an issue at https://github.com/davidshepherd7/frames-only-mode/
to let me know."
:group 'frames-only-mode)

(defcustom frames-only-mode-reopen-frames-from-hidden-x11-virtual-desktops
;; TODO: enable by default when this has had a bit more testing
;; (not (null (and (eq window-system 'x)
;; (executable-find "wmctrl"))))
nil
"When a frame is visible on a hidden virtual desktop, open a new copy of the frame.
This will only work under X11 and when you have the wmctrl binary
available on your path (you can probably install wmctrl from your
operating system's package manager).
It's a bit of a hack, so there may be some issues."
:group 'frames-only-mode)



(defun frames-only-mode-revertable-set (var-vals)
Expand Down Expand Up @@ -198,6 +213,46 @@ Only if there are no other windows in the frame, and if the buffer is in frames-
(defun frames-only-mode-flycheck-display-errors (errors)
(message "%s" (mapcar 'flycheck-error-format-message-and-id errors)))




;;; Interactions with wmctrl

(defun frames-only-mode--call-process (process &rest args)
"Call a process with sensible error handling and output to a string"
(with-output-to-string
(let* ((exit-code (apply #'call-process process nil standard-output nil args)))
(when (not (equal exit-code 0))
(error "Process %s %s exited with error code %s" process args exit-code)))))

(defun frames-only-mode--x-current-desktop ()
"Get the number of the X11 desktop which is visible"
(--> (frames-only-mode--call-process "wmctrl" "-d")
(s-split "\n" it)
(--map (split-string it "\\s-+") it)
(--first (equal (nth 1 it) "*") it)
(car it)))

(defun frames-only-mode--x-visible-window-names ()
"Get a list of X11 windows which are on the visible desktop."
(let ((current-desktop (frames-only-mode--x-current-desktop)))
(--> (frames-only-mode--call-process "wmctrl" "-l")
(s-split "\n" it)
(--map (s-split-up-to "\\s-+" it 3) it)
(--filter (equal (nth 1 it) current-desktop) it)
(--map (nth 3 it) it))))

(defun frames-only-mode--x-buffer-window-visible (buffer-name)
"Check buffer is currently displayed on a visible X11 virtual desktop."
(--some (equal it buffer-name)
(frames-only-mode--x-visible-window-names)))

(defun frames-only-mode--display-buffer-fn (buffer property-alist)
"See `frames-only-mode-reopen-frames-from-hidden-x11-virtual-desktops'."
(when (and frames-only-mode-reopen-frames-from-hidden-x11-virtual-desktops
(not (frames-only-mode--x-buffer-window-visible (buffer-name buffer))))
(display-buffer-pop-up-frame buffer property-alist)))



(defvar frames-only-mode-mode-map
Expand Down Expand Up @@ -243,7 +298,16 @@ Only if there are no other windows in the frame, and if the buffer is in frames-
;; Make sure completions buffer is buried after we are done with the minibuffer
(if frames-only-mode
(add-hook 'minibuffer-exit-hook #'frames-only-mode-bury-completions)
(remove-hook 'minibuffer-exit-hook #'frames-only-mode-bury-completions)))
(remove-hook 'minibuffer-exit-hook #'frames-only-mode-bury-completions))

;; Set up hacks to pop up new frames for buffers when they are displayed on a
;; virtual desktop which is not currently visible (X11 only).
(if frames-only-mode
(add-to-list 'display-buffer-alist
(cons "\*compilation\*" (cons #'frames-only-mode--display-buffer-fn nil)))
(setq display-buffer-alist
(--remove (equal (car (cdr it)) #'frames-only-mode--display-buffer-fn)
display-buffer-alist))))



Expand Down
7 changes: 0 additions & 7 deletions test/frames-only-mode-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@

(require 'frames-only-mode)

(defmacro with-frames-only-mode (&rest body)
`(unwind-protect
(progn
(frames-only-mode 1)
,@body)
(frames-only-mode 0)))

(defmacro fom-rollback-test (expr)
"Check expr before, during and after toggling frames-only-mode.
Expand Down
13 changes: 13 additions & 0 deletions test/test-helper.el
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
(require 'f)

(require 'frames-only-mode (f-expand "frames-only-mode.el" (f-parent (f-dirname (f-this-file)))))

;; For el-mock
(eval-when-compile
(require 'cl))

(require 'el-mock)

(defmacro with-frames-only-mode (&rest body)
`(unwind-protect
(progn
(frames-only-mode 1)
,@body)
(frames-only-mode 0)))
71 changes: 71 additions & 0 deletions test/wmctrl-interactions-test.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@

(ert-deftest current-desktop ()
(with-mock
(mock (frames-only-mode--call-process "wmctrl" "-d") => "
0 - DG: N/A VP: N/A WA: N/A 1
1 * DG: N/A VP: N/A WA: 1920,0 1920x1200 2
2 - DG: N/A VP: N/A WA: N/A 3
3 - DG: N/A VP: N/A WA: N/A dump
")
(should (equal (frames-only-mode--x-current-desktop) "1"))))


(ert-deftest visible-windows ()
(with-mock
(mock (frames-only-mode--x-current-desktop) => "1")
(mock (frames-only-mode--call-process "wmctrl" "-l") => "
0x0200013c 1 cantor Extended Window Manager Hints - Mozilla Firefox
0x01c08469 1 cantor frames-only-mode.el
0x01c0867d 1 cantor *compilation*
0x01c06151 1 cantor wmctrl-interactions-test.el
0x01c0548c 1 cantor *scratch*
0x04000009 1 cantor x-terminal-emulator
0x0200001e 3 cantor Nowena - Amenra - Google Play Music - Mozilla Firefox
0x01c05f98 3 cantor main.c
")
(should (equal (frames-only-mode--x-visible-window-names)
'("Extended Window Manager Hints - Mozilla Firefox"
"frames-only-mode.el"
"*compilation*"
"wmctrl-interactions-test.el"
"*scratch*"
"x-terminal-emulator"
)))
))


(ert-deftest compilation-window-display ()
(with-frames-only-mode

;; This is not running graphically, so we need to override the usual
;; frames-only-mode setting of 'graphic-only and set pop-up-frames to true.
(let ((pop-up-frames t)
(frames-only-mode-reopen-frames-from-hidden-x11-virtual-desktops t))

(get-buffer-create "*compilation*")

;; When the compilation buffer is visible
(with-mock
(mock (frames-only-mode--x-buffer-window-visible "*compilation*") => t)
(mock (make-frame *))
(display-buffer "*compilation*"))

;; When the compilation buffer is not currently visible
(with-mock
(mock (frames-only-mode--x-buffer-window-visible "*compilation*") => nil)
(mock (make-frame *))
(display-buffer "*compilation*")))))


(defun fom/display-buffer-alist-contains-settings ()
(--find (equal (car (cdr it))
#'frames-only-mode--display-buffer-fn)
display-buffer-alist))

(ert-deftest settings-reverted-for-compilation-window-display ()
(should (not (fom/display-buffer-alist-contains-settings)))

(with-frames-only-mode
(should (fom/display-buffer-alist-contains-settings)))

(should (not (fom/display-buffer-alist-contains-settings))))

0 comments on commit cbce743

Please sign in to comment.