~ilmu/vegur

09cb31a5dfa63c71b97d54e8080dcdbecb807530 — ilmu 1 year, 9 months ago 8ac728f
Moving around works as intended now, no need to fiddle with internals more.
M bootstrap-figs/stumpwm/commands.lisp => bootstrap-figs/stumpwm/commands.lisp +45 -18
@@ 2,6 2,7 @@

Commands I want:
----------------
Name window so that s-e (query stack) becomes useful (currently shows bash,bash,bash..)
Open emacs with stumpwm config file $FILE
Open emacs with sly repl into stumpwm
Chaining commands sequentially             :~: (run-commands cmd1 cmd2 ...)


@@ 28,12 29,13 @@ API for datalisp (maybe via swank thread)


;; splits : make necessary split to perform motion given query
;; split -> exchange-windows in direction of split == moving window with split.
;; NOTE use exchange-windows in direction of split == moving window with split.
;; NOTE not needed since the internals don't have to send window when splitting

;; only :: removes all splits.
;; only :~: removes all splits.
;; remove-split
;; hsplit  (-equally -uniformly)
;; vsplit
;; hsplit  (-equally -uniformly)  | These are rewritten to split in hjkl-like
;; vsplit                         | directions. see internals.lisp for details.

;; motions : package queries and splits into useful actions



@@ 60,23 62,46 @@ API for datalisp (maybe via swank thread)
(defcommand window-send-clipboard () ()
  (window-send-string (get-x-selection)))

;; Menu can use stump cli command runner with eval-line
(defcommand eval-selection () ()
;; FIXME: make interactive by echoing selection and doing y-or-n-p.
(defcommand eval-selection () () 
  (eval-line (get-x-selection)))

#| NAVIGATION

(defcommand go-left () ()
  (hsplit)
  (move-window :left))
(defcommand go-right () ()
  (hsplit)
  (move-window :right))
(defcommand go-up () ()
  (vsplit)
  (move-window :up))
(defcommand go-down () ()
  (vsplit)
  (move-window :down))
The idea is that you have stacks of windows inside frames.
You can move focus between frames and then flip through the windows of the frame.
You can also take the window on top with you as you move the focus, if you do then
new frames will be made as necessary by splitting in the desired direction.

Finally, if you move a window from a frame so that the frame becomes empty then
the split will be closed and the frame collapsed.

The idea here is that you can easily rearrange the desktop if you have few
windows in total or a main stack that you split off-of to work.

Groups are only used to send and receive stacks of windows (frames) that are
not required in the current context (group).

The group > frame > window organization can be subverted in future by menus
and tags. Then you can use a hook when switching groups to move windows into
frames on newly active group which are "shared memory" and should also be in
that group. Ideally very few commands are needed for navigation and these
concepts can be avoided by user who just opens, closes and rearranges windows.
|#

(defcommand (dirsplit tile-group) (dir) ((:direction "Direction: "))
"Split the current frame into 2 frames in the desired direction."
  (split-frame-in-dir (current-group) dir 1/2))

(defcommand (move tile-group) (dir) ((:direction "Direction: "))
  "Split the frame if necessary to move in direction. Closes split if it leaves empty frame."
  (when (detect-monitor-edge (current-group) dir)
    (dirsplit dir))
  (move-window dir)
  (fprev)
  (when (detect-empty-frame (current-group))
    (remove-split))
  (fnext))

;; such a soup of accessors, find some simple example to work from... really need repl!
;;( (frame-windows (current-group) (tile-group-current-frame (current-group)))


@@ 86,3 111,5 @@ API for datalisp (maybe via swank thread)
                     ;; args 
(defcommand dump-top-map () () ;; interactive args
  (with-open-file (s (append-to-path *data-dir* "top-map.sexp") :direction :output)))



M bootstrap-figs/stumpwm/config => bootstrap-figs/stumpwm/config +3 -3
@@ 30,9 30,9 @@
;; TODO: causal relation between keyboard options in guix system config and key-bindings in stump
;;       will be kept track of in the datalisp template logic that generates the configuration file.

(load-config-file "internals.lisp");; patches to stump core, useful for commands.
(load-config-file "commands.lisp") ;; define various commands that can be bound.
(load-config-file "keymaps.lisp")  ;; bind commands to keys in keymaps to interact.
(load-config-file "internals.lisp") ;; patches to stump core, useful for commands.
(load-config-file "commands.lisp")  ;; define various commands that can be bound.
(load-config-file "keymaps.lisp")   ;; bind commands to keys in keymaps to interact.

#| bind keys to *root-map* for C-t prefixed commands and *top-map* for unprefixed commands.
(stumpwm:define-key stumpwm:*root-map* (stumpwm:kbd "u") "exec kitty")

M bootstrap-figs/stumpwm/internals.lisp => bootstrap-figs/stumpwm/internals.lisp +61 -30
@@ 7,16 7,20 @@ A real solution would rewrite the internals rather than hack around it like this
|#


(defun split-frame-dir (group p dir ratio)

;; splitting functions

(defun split-frame-dir (group dir ratio)
  "Return 2 new frames. The first one stealing P's number and window"
  (let* ((w (if (or (eq dir :left)
  (let* ((p  (tile-group-current-frame group))
	 (w (if (or (eq dir :left)
		    (eq dir :right))
		(ratio-or-pixel (frame-width p) ratio)
		(frame-width p))
		(frame-width p)))
         (h (if (or (eq dir :up)
		    (eq dir :down))
		(ratio-or-pixel (frame-height p) ratio)
		(frame-height p))
		(frame-height p)))
	 (x (if (eq dir :right)
		(+ (frame-x p) w)
		(frame-x p)))


@@ 41,44 45,32 @@ A real solution would rewrite the internals rather than hack around it like this
    (when (eq dir :left)
      (setf (frame-x p) (+ (frame-x p) w)))
    ;; bureaucracy
    (run-hook-with-args *new-frame-hook* f)
    (if (or (eq dir :up) (eq dir :left))
	(run-hook-with-args *split-frame-hook* p f p)
	(run-hook-with-args *split-frame-hook* p p f))
    (run-hook-with-args *new-frame-hook* f)
    (values p f)))

(defun split-frame (group how &optional (ratio 1/2))
(defun split-frame (group dir &optional (ratio 1/2))
  "Split the current frame into 2 frames. Return new frame number, if
it succeeded. NIL otherwise. RATIO is a fraction of the screen to
allocate to the new split window. If ratio is an integer then the
number of pixels will be used. This can be handy to setup the
desktop when starting."
  (check-type how (member :row :column :up :down :right :left))
  (check-type dir (member :row :column :up :down :right :left))
  (let* ((frame (tile-group-current-frame group))
         (head (frame-head group frame))
	 ;; once we've created split we need to move window provenance
	 (migrate-to-children-frames
	   (lambda (f1 f2)
	     (migrate-frame-windows group frame f1)
	     (choose-new-frame-window f2 group) ;; moves a window to new frame
	     (when (eq frame (tile-group-current-frame group))
	       (setf (tile-group-current-frame group) f1))
	     (setf (tile-group-last-frame group) f2)
	     (sync-frame-windows group f1)
	     (sync-frame-windows group f2)
	     ;; we also need to show the window we moved to the new frame
	     (when (frame-window f2)
	       (unhide-window (frame-window f2)))
	     (frame-number f2))))
         (head (frame-head group frame)))
    ;; backwards compat
    (when (eq how :row) (setf how :right))
    (when (eq how :column)  (setf how :down))
    (when (eq dir :row)     (setf dir :right))
    (when (eq dir :column)  (setf dir :down))
    ;; don't create frames smaller than the minimum size
    (when (or (and (member how '(:up :down))
    (when (or (and (member dir '(:up :down))
		   (>= (frame-height frame) (* *min-frame-height* 2)))
	      (and (member how '(:left :right))
	      (and (member dir '(:left :right))
		   (>= (frame-width frame) (* *min-frame-width* 2))))
      (multiple-value-bind (f1 f2) (split-frame-dir group frame how ratio)
      (multiple-value-bind (f1 f2) (split-frame-dir group dir ratio)
	;; swap f1 and f2 when we insert new window above or before old window
	(when (or (eq dir :left) (eq dir :up)) (rotatef f1 f2))
	(setf (tile-group-frame-head group head)
		 (if (atom (tile-group-frame-head group head))
		     (list f1 f2)


@@ 88,10 80,49 @@ desktop when starting."
				      (lambda (tree)
					(unless (atom tree)
					  (find frame tree))))))
	(if (member how '(:right :down))
	    (funcall migrate-to-children-frames f1 f2)
	    (funcall migrate-to-children-frames f2 f1))))))
	;; undo swap, the windows stay in the resized parent window
	(when (or (eq dir :left) (eq dir :up)) (rotatef f1 f2))
	(migrate-frame-windows group frame f1)
	;; NOTE: this default is noisy for my intended purposes
	;; (choose-new-frame-window f2 group) ;; moves a window to new frame
	(when (eq frame (tile-group-current-frame group))
	  (setf (tile-group-current-frame group) f1))
	(setf (tile-group-last-frame group) f2)
	(sync-frame-windows group f1)
	(sync-frame-windows group f2)
	;; we also need to show the window we moved to the new frame (if we did)
	(when (frame-window f2)
	  (unhide-window (frame-window f2)))
	(frame-number f2)))))

    
(defun split-frame-in-dir (group dir &optional (ratio 1/2))
  (let ((f (tile-group-current-frame group)))
    (if (split-frame group dir ratio)
        (progn
          (when (frame-window f)
            (update-decoration (frame-window f)))
          (show-frame-indicator group))
        (message "Cannot split smaller than minimum size."))))  


;; query functions

(defun detect-monitor-edge (group direction)
  "Checks if there is a frame in the given direction"
  (let ((frame (tile-group-current-frame group)))
    (move-focus direction)
    (if (eq (frame-number frame)
	    (frame-number (tile-group-current-frame group)))
	t
	(progn
	  (fprev)
	  nil))))

(defun detect-empty-frame (group)
  "Checks if the current frame has only one window"
  (= 0 (length (frame-windows (current-group) (tile-group-current-frame (current-group))))))

(defcommand test-detection () ()
  (when (detect-monitor-edge (current-group) :up)
    (echo "something")))

M bootstrap-figs/stumpwm/keymaps.lisp => bootstrap-figs/stumpwm/keymaps.lisp +26 -8
@@ 26,34 26,52 @@ TODO: List all actions I need
(define-key *root-map* (kbd "z") "exec kitty")

;; TOP MAP - META = MODIFY
(define-key *top-map* (kbd "M-e") "eval-selection") ;; fixme
(define-key *top-map* (kbd "M-n") "gnew")
(define-key *top-map* (kbd "M-w") "pull-from-windowlist")
(define-key *top-map* (kbd "M-Up") "gnext")
(define-key *top-map* (kbd "M-Down") "gprev")
(define-key *top-map* (kbd "M-h") "move-window left")
(define-key *top-map* (kbd "M-l") "move-window right")
(define-key *top-map* (kbd "M-j") "move-window down")
(define-key *top-map* (kbd "M-k") "move-window up")
(define-key *top-map* (kbd "M-h") "move left")
(define-key *top-map* (kbd "M-l") "move right")
(define-key *top-map* (kbd "M-j") "move down")
(define-key *top-map* (kbd "M-k") "move up")

;; TOP MAP - SUPER = TRAVERSE
(define-key *top-map* (kbd "s-z") "exec kitty")
(define-key *top-map* (kbd "s-x") "exec")
(define-key *top-map* (kbd "s-c") "colon")
(define-key *top-map* (kbd "s-f") "fullscreen")
(define-key *top-map* (kbd "s-Up") "gnext")
(define-key *top-map* (kbd "s-Down") "gprev")
(define-key *top-map* (kbd "s-e") "echo-frame-windows")
(define-key *top-map* (kbd "s-b") "prev-in-frame")
(define-key *top-map* (kbd "s-n") "next-in-frame")
(define-key *top-map* (kbd "s-h") "move-focus left")
(define-key *top-map* (kbd "s-h") "move-focus left")
(define-key *top-map* (kbd "s-l") "move-focus right")
(define-key *top-map* (kbd "s-j") "move-focus down")
(define-key *top-map* (kbd "s-k") "move-focus up")
(define-key *top-map* (kbd "s-S-h") "move left")
(define-key *top-map* (kbd "s-S-l") "move right")
(define-key *top-map* (kbd "s-S-j") "move down")
(define-key *top-map* (kbd "s-S-k") "move up")

#| REARRANGE MODE

Ideally I could use a,s,d,f,q,w,e,r as modifiers for hjkl while in the mode.

a: 
s: move with split even if not at screen edge
d: delete the split and move entire frame with focus (same as if only one window was in frame when moving)
f: float window and move it as if being dragged around by the mouse, single tap f would cycle focus of floating
q: query focus for context and open results in frame direction from focus. (or query for relation to frame dir)
w: window moves with focus (same as shift would do I suppose)
e: entire frame moves with focus, swaps frames rather than deleting splits like d does.
r: resize focused window

#|
;; REARRANGE MODE
(define-interactive-keymap (rearrange tile-group) (:on-enter #'enter-rearrange-mode
						   :on-exit #'exit-rearrange-mode
						   :abort-if #'abort-rearrange-mode-p)
  ((kbd "j") "move window up somehow")
  ((kbd "k") "move window up somehow")
  ...)
|#