boxes.lisp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. ;;;; boxes.lisp
  2. (in-package #:boxes)
  3. (defvar *views* '())
  4. (defvar *displayingp* nil)
  5. (defclass primitive-box ()
  6. ((visiblep :reader visiblep :initform nil)
  7. (view :reader view :initform nil)
  8. (keymap :reader keymap :initform (keymap:make-keymap))
  9. (secondary-keymaps :accessor secondary-keymaps :initform '())))
  10. (defun (setf view) (view box)
  11. (check-type view primitive-box)
  12. (setf (slot-value box 'view) view)
  13. (dolist (c (children box))
  14. (when c
  15. (setf (view c) view)))
  16. view)
  17. (defmethod (setf visiblep) (value (box primitive-box))
  18. (dolist (c (children box))
  19. (when c
  20. (setf (visiblep c) value)))
  21. (setf (slot-value box 'visiblep) value))
  22. (defclass window-box (primitive-box)
  23. ((window :accessor window :initarg :window :initform nil)
  24. (stumpwm-frame :reader stumpwm-frame :initform (stumpwm::make-frame :number 0
  25. :x 0
  26. :y 0
  27. :width *screen-width*
  28. :height *screen-height*
  29. :window nil))))
  30. (defun frame-window-sync (frame window)
  31. (setf (stumpwm::frame-window frame) window)
  32. (when window
  33. (setf (stumpwm::window-frame window) frame)))
  34. (defmethod initialize-instance :after ((box window-box) &rest initargs)
  35. (declare (ignore initargs))
  36. (frame-window-sync (stumpwm-frame box) (window box)))
  37. (defmethod (setf window) :after ((window stumpwm:window) (box window-box))
  38. (frame-window-sync (stumpwm-frame box) (window box)))
  39. (defgeneric set-screen-position (box x y width height))
  40. (defmethod set-screen-position ((box window-box) x y width height)
  41. (check-type x integer)
  42. (check-type y integer)
  43. (check-type width integer)
  44. (check-type height integer)
  45. ;; todo compare with the screen dimensions
  46. (let ((frame (stumpwm-frame box)))
  47. (setf (stumpwm:frame-x frame) x
  48. (stumpwm:frame-y frame) y
  49. (stumpwm:frame-width frame) width
  50. (stumpwm:frame-height frame) height)))
  51. (defclass double-box (primitive-box)
  52. ((split-ratio :accessor split-ratio :initarg :split-ratio :initform 1/2)
  53. (split-direction :reader split-direction :initarg :split-direction :initform (error "Missing split direction!"))
  54. (1st :accessor 1st :initarg :1st)
  55. (2nd :accessor 2nd :initarg :2nd)
  56. (current-child :accessor current-child :initform nil)))
  57. (defmethod set-screen-position ((box double-box) x y width height)
  58. (flet ((new-params (coord dimension)
  59. (let* ((new-dimension-0 (truncate (* (split-ratio box)
  60. dimension)))
  61. (new-dimension-1 (- dimension new-dimension-0))
  62. (new-coord-1 (+ coord new-dimension-0)))
  63. (values new-dimension-0 new-dimension-1 new-coord-1))))
  64. (if (eql (split-direction box) :column)
  65. (multiple-value-bind (new-dimension-0 new-dimension-1 new-coord-1) (new-params y height)
  66. (set-screen-position (1st box) x y width new-dimension-0)
  67. (set-screen-position (2nd box) x new-coord-1 width new-dimension-1))
  68. (multiple-value-bind (new-dimension-0 new-dimension-1 new-coord-1) (new-params x width)
  69. (set-screen-position (1st box) x y new-dimension-0 height)
  70. (set-screen-position (2nd box) new-coord-1 y new-dimension-1 height)))))
  71. (defgeneric children (box))
  72. (defmethod children ((box primitive-box))
  73. '())
  74. (defmethod children ((box double-box))
  75. (list (1st box) (2nd box)))
  76. (defmethod (setf 1st) :before (child (box double-box))
  77. (check-type child primitive-box)
  78. (when (visiblep box)
  79. (setf (visiblep (1st box)) nil)))
  80. (defmethod (setf 1st) :after (child (box double-box))
  81. (check-type child primitive-box)
  82. (when (visiblep box)
  83. (display (view box))))
  84. (defmethod (setf 2nd) :before (child (box double-box))
  85. (check-type child primitive-box)
  86. (when (visiblep box)
  87. (setf (visiblep (2nd box)) nil)))
  88. (defmethod (setf 2nd) :after (child (box double-box))
  89. (check-type child primitive-box)
  90. (when (visiblep box)
  91. (display (view box))))
  92. (defun descendants (box)
  93. (let ((children (children box)))
  94. (cons box
  95. (loop for c in children
  96. append (descendants c)))))
  97. (defparameter *screen-width* (stumpwm:screen-width (stumpwm:current-screen)))
  98. (defparameter *screen-height* (stumpwm:screen-height (stumpwm:current-screen)))
  99. (defun display (box &optional (previous-box nil previous-box-supplied-p))
  100. (if (typep box 'dialog-box)
  101. (progn
  102. (if previous-box-supplied-p
  103. (display (dialog-parent-box box) previous-box)
  104. (display (dialog-parent-box box)))
  105. (stumpwm:focus-window (window box)))
  106. (unless *displayingp*
  107. (let ((*displayingp* t))
  108. (when previous-box-supplied-p
  109. (setf (visiblep previous-box) nil))
  110. (setf (visiblep box) t)
  111. (setf (view box) box)
  112. (set-screen-position box 0 0 *screen-width* *screen-height*)
  113. (let ((frames (loop for d in (descendants box)
  114. when (typep d 'window-box)
  115. collect (stumpwm-frame d))))
  116. (draw-frames frames (stumpwm:current-group)))
  117. (values box previous-box)))))
  118. #|
  119. (defgeneric current (box))
  120. (defmethod current ((box box))
  121. box)
  122. (defmethod current ((box double-box))
  123. (current-child box))
  124. (defgeneric ensure-current (box))
  125. (defmethod ensure-current ((box box))
  126. box)
  127. (defmethod ensure-current ((box double-box))
  128. (unless (member (current-child box)
  129. (list (1st box) (2nd box)))
  130. (ensure-current (1st box))))
  131. (defgeneric minimal-current (box))
  132. (defmethod minimal-current ((box box))
  133. (current box))
  134. (defmethod minimal-current ((box double-box))
  135. (minimal-current (current box)))
  136. |#
  137. (defgeneric box-to-frame (box x y width height))
  138. (defclass box (primitive-box)
  139. ((contents :reader contents :initarg :contents :initform (make-instance 'primitive-box))))
  140. (defmethod (setf contents) (contents (box box))
  141. (check-type contents primitive-box)
  142. (when (and (visiblep box) (contents box))
  143. (setf (visiblep (contents box)) nil))
  144. (setf (slot-value box 'contents) contents)
  145. (when (visiblep box)
  146. (display (view box))))
  147. (defmethod set-screen-position ((box box) x y width height)
  148. (set-screen-position (contents box) x y width height))
  149. (defmethod children ((box box))
  150. (list (contents box)))
  151. (defun nonempty-list-of-frames-p (thing)
  152. (and thing
  153. (listp thing)
  154. (every #'stumpwm::frame-p thing)))
  155. (in-package #:stumpwm)
  156. (defun boxes::draw-frames (frames group)
  157. (check-type frames (satisfies boxes::nonempty-list-of-frames-p))
  158. (check-type group tile-group)
  159. (let ((windows (group-windows group)))
  160. (labels ((give-frame-a-window (f)
  161. (unless (frame-window f)
  162. (setf (frame-window f) (find f windows :key 'window-frame)))))
  163. (loop for i from 0
  164. for frame in frames
  165. do (setf (frame-number frame) i))
  166. (setf (tile-group-frame-tree group) (list frames))
  167. (setf (tile-group-current-frame group) (find 0 (group-frames group) :key 'frame-number) )
  168. ;; give any windows still not in a frame a frame
  169. ;; otherwise BOOOOOOM!
  170. (dolist (w windows)
  171. (unless (window-frame w)
  172. (setf (window-frame w) (tile-group-current-frame group))))
  173. ;; FIXME: if the current window was blank in the dump, this does not honour that.
  174. (give-frame-a-window (tile-group-current-frame group))
  175. ;; raise the curtains
  176. (let ((visible-windows (loop for frame in (group-frames group)
  177. when (frame-window frame)
  178. collect it)))
  179. (dolist (w visible-windows)
  180. (unhide-window w))
  181. (dolist (w (set-difference windows visible-windows))
  182. (hide-window w))
  183. )
  184. (sync-all-frame-windows group)
  185. (focus-frame group (tile-group-current-frame group)))))
  186. (in-package #:boxes)
  187. (defstruct (names (:conc-name nil))
  188. (names-to-objects (make-hash-table :test 'equalp))
  189. (objects-to-names (make-hash-table :test 'eq)))
  190. (defvar *names*)
  191. (defun by-name (object &optional (names *names*))
  192. (gethash object (names-to-objects names)))
  193. (defun name (name &optional (names *names*))
  194. (gethash name (objects-to-names names)))
  195. (defun (setf name) (name object &optional (names *names*))
  196. (remhash (name object names) (names-to-objects names))
  197. (if (null name)
  198. (remhash object (objects-to-names names))
  199. (setf (gethash name (names-to-objects names)) object
  200. (gethash object (objects-to-names names)) name))
  201. name)
  202. (defvar *view-names* (make-names))
  203. (defgeneric view-name (view))
  204. (defmethod view-name (view)
  205. (name view *view-names*))
  206. (defgeneric (setf view-name) (name view))
  207. (defmethod (setf view-name) (name view)
  208. (setf (name view *view-names*) name))
  209. (defun view-by-name (name)
  210. (by-name name *view-names*))
  211. (defvar *view* (make-instance 'box))
  212. (defun current-view ()
  213. *view*)
  214. (defun (setf current-view) (box)
  215. (check-type box primitive-box)
  216. (display box *view*)
  217. (setf *view* box)
  218. (setf *views* (cons box (delete box *views*)))
  219. (keymap:install (apply #'keymap:make-keymap
  220. (keymap box)
  221. (append (secondary-keymaps box)
  222. (list *global-shortcuts*))))
  223. box)
  224. (defclass dialog-box (window-box)
  225. ((parent :reader dialog-parent-box :initarg :parent :initform (error "Vbi sunt parentes mei?"))))
  226. (defun register-window-box (window)
  227. (let ((box (if (eql (stumpwm:window-type window) :dialog)
  228. (make-instance 'dialog-box :window window :parent *view*)
  229. (make-instance 'window-box :window window))))
  230. (setf (current-view) box)))
  231. (defun update-window-boxes (&rest args)
  232. (declare (ignore args))
  233. (let (parent)
  234. (setf *views* (remove-if (lambda (view)
  235. (if (and (typep view 'window-box)
  236. (window view)
  237. (zerop (stumpwm:window-state (window view))))
  238. (progn
  239. (when (typep view 'dialog-box)
  240. (setf parent (dialog-parent-box view)))
  241. t)
  242. nil))
  243. *views*))
  244. (unless (member *view* *views*)
  245. (setf (current-view) (or parent (first *views*))))))
  246. (stumpwm:add-hook stumpwm:*new-window-hook* 'register-window-box)
  247. (stumpwm:add-hook stumpwm:*destroy-window-hook* 'update-window-boxes)
  248. (defun select-view-from-menu (&optional (initial-selection 0))
  249. (second (stumpwm:select-from-menu (stumpwm:current-screen)
  250. (loop for view in *views*
  251. collect (list (or (view-name view)
  252. (and (typep view 'window-box)
  253. (stumpwm:window-title (window view)))
  254. (write-to-string view)) view))
  255. " Views:"
  256. initial-selection)))
  257. (defun rresize (q)
  258. "rresize"
  259. (setf (split-ratio *view*) (* q (split-ratio *view*)))
  260. (setf (current-view) *view*))
  261. #+nil (defmethod view-name ((box box-with-window))
  262. (let* ((title (stumpwm:window-title (window box)))
  263. (length (length title)))
  264. (if (< length 13)
  265. title
  266. (format nil "~A..~A" (subseq title 0 5) (subseq title (- length 5))))))
  267. (defgeneric delete-view (view))
  268. (defmethod delete-view (view)
  269. (setf *views* (delete view *views*))
  270. (when (eql view *view*)
  271. (setf (current-view) (first *views*))))
  272. (defmethod delete-view ((view window-box))
  273. (stumpwm:delete-window (window view))
  274. (call-next-method))
  275. ;; TODO lock
  276. (defmacro defkey ((key &optional (keymap (keymap *view*))) &body body)
  277. `(keymap:add-binding ,key (lambda () ,@body) ,keymap))
  278. (defvar *global-shortcuts* (keymap:make-keymap))
  279. (defun add-global-shortcut (key thunk)
  280. (keymap:add-binding key thunk *global-shortcuts*))
  281. (defmacro define-global-shortcut (key &body body)
  282. `(add-global-shortcut ,key (lambda () ,@body)))
  283. (define-global-shortcut "M-s-Left" (rresize 0.99))
  284. (define-global-shortcut "M-s-Right" (rresize 1.01))
  285. (define-global-shortcut "M-Tab" (setf (current-view) (second *views*)))
  286. (define-global-shortcut "F4" (delete-view *view*))
  287. (defun window-pid (w)
  288. (first (stumpwm::window-property w :_NET_WM_PID)))
  289. (defun launch-catch-window (command &optional (timeout 10))
  290. (let ((process (uiop:launch-program command :force-shell nil)))
  291. (let ((pid (uiop:process-info-pid process)))
  292. (loop repeat (floor (* timeout 20))
  293. for windows = (stumpwm:group-windows (stumpwm:current-group))
  294. for my-window = (find pid windows :key #'window-pid)
  295. do (sleep 0.1)
  296. when my-window do (return (values process my-window))
  297. finally (return (values process nil))))))
  298. (defun resolve-view-designator (designator)
  299. (etypecase designator
  300. (primitive-box designator)
  301. (string (or (view-by-name designator) (make-instance 'window-box)))
  302. (null (make-instance 'window-box))))
  303. (defun hb (&optional (left *view*) (right (second *views*)) (ratio 1/2))
  304. (make-instance 'double-box
  305. :split-direction :row
  306. :split-ratio ratio
  307. :1st (resolve-view-designator left)
  308. :2nd (resolve-view-designator right)))
  309. (defun hb! (&optional (left *view*) (right (second *views*)) (ratio 1/2))
  310. (setf (current-view) (hb left right ratio)))
  311. (defun vb (&optional (top *view*) (bottom (second *views*)) (ratio 1/2))
  312. (make-instance 'double-box
  313. :split-direction :column
  314. :split-ratio ratio
  315. :1st (resolve-view-designator top)
  316. :2nd (resolve-view-designator bottom)))
  317. (defun vb! (&optional (top *view*) (bottom (second *views*)) (ratio 1/2))
  318. (setf (current-view) (vb top bottom ratio)))
  319. (defun vm (&optional (initial-selection 0))
  320. (select-view-from-menu initial-selection))
  321. (defun n! (name &optional (view *view*))
  322. (setf (view-name view) name))
  323. (define-symbol-macro %
  324. *view*)
  325. (define-symbol-macro %%
  326. (second *views*))
  327. (define-symbol-macro %%%
  328. (third *views*))
  329. (define-symbol-macro _
  330. (vm))
  331. (define-symbol-macro {
  332. (1st *view*))
  333. (define-symbol-macro }
  334. (2nd *view*))
  335. (defun {! (view)
  336. (setf (1st *view*) (resolve-view-designator view)))
  337. (defun }! (view)
  338. (setf (2nd *view*) (resolve-view-designator view)))
  339. (define-global-shortcut "F12" (let ((view (select-view-from-menu 1)))
  340. (when view
  341. (setf (current-view) view))))