1
1
;; wm-spec.jl -- implement the new (GNOME/KDE) wm hints spec
3
;; $Id: wm-spec.jl,v 1.27 2001/12/09 22:44:22 jsh Exp $
3
;; $Id: wm-spec.jl,v 1.48 2003/11/25 04:01:15 jsh Exp $
5
5
;; Copyright (C) 1999, 2000 John Harper <john@dcs.warwick.ac.uk>
32
33
sawfish.wm.workspace
33
34
sawfish.wm.viewport
34
sawfish.wm.state.maximize
35
sawfish.wm.state.iconify)
35
sawfish.wm.state.iconify
36
sawfish.wm.util.workarea)
40
;; - _NET_WM_NAME -- needs to be in C code?
43
;; - _NET_WM_ALLOWED_ACTIONS
45
;; - _NET_WM_MOVERESIZE changes
46
;; - _NET_SHOWING_DESKTOP?
47
;; - _NET_MOVERESIZE_WINDOW
44
49
;; maybe add some state extensions for things the spec doesn't
45
50
;; cover but existed in the old GNOME spec; e.g. _GNOME_WM_STATE_FOO
46
51
;; for FOO being DO_NOT_COVER, SKIP_FOCUS, ..?
57
62
(defconst _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6)
58
63
(defconst _NET_WM_MOVERESIZE_SIZE_LEFT 7)
59
64
(defconst _NET_WM_MOVERESIZE_MOVE 8)
65
(defconst _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9)
66
(defconst _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10)
61
68
(defconst _NET_WM_STATE_REMOVE 0)
62
69
(defconst _NET_WM_STATE_ADD 1)
65
72
(define wm-spec-window-id nil)
67
(define supported-protocols
68
[_NET_CLIENT_LIST _NET_CLIENT_LIST_STACKING _NET_NUMBER_OF_DESKTOPS
69
_NET_DESKTOP_GEOMETRY _NET_DESKTOP_VIEWPORT _NET_CURRENT_DESKTOP
70
_NET_DESKTOP_NAMES _NET_ACTIVE_WINDOW _NET_CLOSE_WINDOW
71
_NET_WM_MOVERESIZE _NET_WM_DESKTOP _NET_WM_WINDOW_TYPE _NET_WM_STATE])
74
(define supported-atoms
77
_NET_CLIENT_LIST_STACKING
83
_NET_NUMBER_OF_DESKTOPS
87
_NET_SUPPORTING_WM_CHECK
92
_NET_WM_MOVERESIZE_MOVE
93
_NET_WM_MOVERESIZE_SIZE_BOTTOM
94
_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT
95
_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT
96
_NET_WM_MOVERESIZE_SIZE_LEFT
97
_NET_WM_MOVERESIZE_SIZE_RIGHT
98
_NET_WM_MOVERESIZE_SIZE_TOP
99
_NET_WM_MOVERESIZE_SIZE_TOPLEFT
100
_NET_WM_MOVERESIZE_SIZE_TOPRIGHT
101
_NET_WM_MOVERESIZE_SIZE_KEYBOARD
102
_NET_WM_MOVERESIZE_MOVE_KEYBOARD
108
_NET_WM_STATE_FULLSCREEN
109
_NET_WM_STATE_MAXIMIZED
110
_NET_WM_STATE_MAXIMIZED_HORZ
111
_NET_WM_STATE_MAXIMIZED_VERT
114
_NET_WM_STATE_SKIP_PAGER
115
_NET_WM_STATE_SKIP_TASKBAR
120
_NET_WM_WINDOW_TYPE_DESKTOP
121
_NET_WM_WINDOW_TYPE_DIALOG
122
_NET_WM_WINDOW_TYPE_DOCK
123
_NET_WM_WINDOW_TYPE_TOOLBAR
124
_NET_WM_WINDOW_TYPE_MENU
125
_NET_WM_WINDOW_TYPE_UTILITY
126
_NET_WM_WINDOW_TYPE_SPLASH])
73
(defconst desktop-layer -4)
74
(defconst dock-layer +4)
128
(defvar wm-spec-below-depth -2)
129
(defvar wm-spec-above-depth +2)
76
131
(define supported-states '())
101
156
(define last-workspace-names nil)
102
157
(define last-area nil)
103
158
(define last-area-count nil)
159
(define last-workarea nil)
160
(define last-showing-desktop nil)
162
(define (update-window-workspace-hints w #!key (limits (workspace-limits)))
163
(let ((vec (if (window-sticky-p/workspace w)
165
(let ((space (or (window-get w 'swapped-in)
166
(car (window-workspaces w)))))
167
(and space (vector (- space (car limits))))))))
168
(unless (equal vec (window-get w 'wm-spec/last-workspace))
170
(set-x-property w '_NET_WM_DESKTOP vec 'CARDINAL 32)
171
(delete-x-property w '_NET_WM_DESTOP))
172
(window-put w 'wm-spec/last-workspace vec))))
105
174
(define (update-workspace-hints)
106
175
(let* ((limits (workspace-limits))
107
176
(port (screen-viewport))
108
177
(port-size viewport-dimensions)
109
(total-workspaces (1+ (- (cdr limits) (car limits)))))
178
(total-workspaces (1+ (- (cdr limits) (car limits))))
179
(workarea (make-vector (* 4 total-workspaces)))
180
(showing-desktop (showing-desktop-p)))
111
182
(define (set-ws-hints)
112
183
;; _NET_NUMBER_OF_DESKTOPS
119
190
(unless (equal last-workspace-names workspace-names)
120
191
(setq last-workspace-names workspace-names)
121
192
(set-x-text-property 'root '_NET_DESKTOP_NAMES
122
(apply vector workspace-names)))
193
(apply vector workspace-names)
124
196
;; _NET_CURRENT_DESKTOP
125
197
(unless (equal last-workspace
144
216
(set-x-property 'root '_NET_DESKTOP_VIEWPORT
145
217
view 'CARDINAL 32)
146
218
(aset view (* i 2) (* (car port) (screen-width)))
147
(aset view (1+ (* i 2)) (* (cdr port) (screen-width)))
219
(aset view (1+ (* i 2)) (* (cdr port) (screen-height)))
223
(unless (equal last-workarea workarea)
224
(set-x-property 'root '_NET_WORKAREA workarea 'CARDINAL 32)
225
(setq last-workarea workarea))
227
;; _NET_SHOWING_DESKTOP
228
(unless (equal showing-desktop last-showing-desktop)
229
(set-x-property 'root '_NET_SHOWING_DESKTOP
230
(vector (if showing-desktop 1 0)) 'CARDINAL 32)
231
(setq last-showing-desktop showing-desktop)))
150
233
(define (set-window-hints w)
151
(cond ((window-sticky-p/workspace w)
152
(set-x-property w '_NET_WM_DESKTOP
153
(vector #xffffffff) 'CARDINAL 32))
154
((window-get w 'swapped-in)
155
;; XXX the gnome-wm standard sucks..!
156
(let ((space (window-get w 'swapped-in)))
157
(set-x-property w '_NET_WM_DESKTOP
158
(vector (- space (car limits)))
234
(update-window-workspace-hints w #:limits limits))
236
;; calculate workareas
238
((= i total-workspaces))
239
(let ((area (calculate-workarea-from-struts
240
#:workspace (+ i (car limits)))))
241
(aset workarea (+ (* i 4) 0) (nth 0 area))
242
(aset workarea (+ (* i 4) 1) (nth 1 area))
243
(aset workarea (+ (* i 4) 2) (- (nth 2 area) (nth 0 area)))
244
(aset workarea (+ (* i 4) 3) (- (nth 3 area) (nth 1 area)))))
161
246
;; apparently some pagers don't like it if we place windows
162
247
;; on (temporarily) non-existent workspaces
184
269
;;; setting the window state hints
186
271
(define (update-client-state w)
187
(let ((state (filter (lambda (x) (not (supported-state-p x)))
188
(window-get w 'wm-spec-last-states))))
189
273
(mapc (lambda (x)
190
274
(when (and (not (pseudo-state-p x))
191
275
(call-state-fun w x 'get))
192
276
(setq state (cons x state))))
193
277
supported-states)
194
(set-x-property w '_NET_WM_STATE (apply vector state) 'ATOM 32)
195
(window-put w 'wm-spec-last-states state)))
278
(set-x-property w '_NET_WM_STATE (apply vector state) 'ATOM 32)))
198
281
;;; honouring the initially set window state hints
201
284
(when (>= (length geom) 2)
202
285
(window-put w 'icon-position (cons (aref geom 0) (aref geom 1)))))
204
(define (wm-class-hacks w)
205
(let ((class (get-x-text-property w 'WM_CLASS)))
206
(when (and class (>= (length class) 2))
207
(cond ((or (and (string= (aref class 1) "Panel")
208
(string= (aref class 0) "panel_window"))
209
(and (string= (aref class 1) "kicker")
210
(string= (aref class 0) "Panel")))
211
(window-put w 'focus-click-through t)
212
(window-put w 'avoid t)
213
(window-put w 'no-history t)
214
(window-put w 'never-iconify t)
215
(window-put w 'never-maximize t)
216
(window-put w 'sticky t)
217
(window-put w 'sticky-viewport t)
218
;; XXX see gnome.jl for why this is needed..
219
(window-put w 'placed t))
220
((string= (aref class 1) "gmc-desktop-icon")
221
(window-put w 'never-focus t)
222
(window-put w 'never-iconify t)
223
(window-put w 'never-maximize t))))))
287
(define (update-strut w)
288
(let ((strut (get-x-property w '_NET_WM_STRUT)))
289
(when (and strut (eq (nth 0 strut) 'CARDINAL))
290
(let ((data (nth 2 strut)))
291
(define-window-strut w (aref data 0) (aref data 2)
292
(aref data 1) (aref data 3))))))
225
294
(define (honour-client-state w)
226
;; things the wm-hints doesn't supply
229
295
(let ((space (get-x-property w '_NET_WM_DESKTOP)))
231
297
(setq space (aref (nth 2 space) 0))
250
316
(setq state (nth 2 state))
251
317
(do ((i 0 (1+ i)))
252
318
((= i (length state)))
253
(call-state-fun w (aref state i) 'init))
254
(window-put w 'wm-spec-last-states (vector->list state))))
319
(call-state-fun w (aref state i) 'init))))
256
323
(let ((geom (get-x-property w '_NET_WM_ICON_GEOMETRY)))
261
328
;;; helper functions
263
(define (define-wm-spec-window-type x fun) (put x 'wm-spec-type fun))
330
(define (define-wm-spec-window-type x fun)
332
(mapc (lambda (y) (define-wm-spec-window-type y fun)) x)
333
(put x 'wm-spec-type fun)))
265
335
(define (define-wm-spec-window-state x fun #!key pseudo)
266
336
(put x 'wm-spec-state fun)
280
(define-wm-spec-window-type
281
'_NET_WM_WINDOW_TYPE_DESKTOP
283
(require 'sawfish.wm.stacking)
284
(mark-window-as-desktop w)
285
(window-put w 'fixed-position t)
286
;; I thought these would be set by the application, but KDE doesn't..
287
(window-put w 'type 'unframed)
288
(window-put w 'sticky t)
289
(window-put w 'sticky-viewport t)
290
(set-window-depth w desktop-layer)))
292
(define-wm-spec-window-type
293
'_NET_WM_WINDOW_TYPE_DOCK
295
(require 'sawfish.wm.stacking)
296
(set-window-depth w dock-layer)
297
(window-put w 'window-list-skip t)
298
(window-put w 'cycle-skip t)))
300
(define-wm-spec-window-type
301
'_NET_WM_WINDOW_TYPE_DIALOG
303
(require 'sawfish.wm.frames)
304
(set-window-type w 'transient)))
306
(define-wm-spec-window-state
307
'_NET_WM_STATE_STICKY
350
(define-wm-spec-window-type '_NET_WM_WINDOW_TYPE_DESKTOP
352
(mark-window-as-desktop w)))
354
(define-wm-spec-window-type '_NET_WM_WINDOW_TYPE_DOCK
356
(mark-window-as-dock w)))
358
(define-wm-spec-window-type '_NET_WM_WINDOW_TYPE_DIALOG
360
(mark-window-as-transient w)))
362
(define-wm-spec-window-type '_NET_WM_WINDOW_TYPE_UTILITY
364
(require 'sawfish.wm.frames)
365
(set-window-type w 'utility)))
367
(define-wm-spec-window-type '_NET_WM_WINDOW_TYPE_TOOLBAR
369
(require 'sawfish.wm.frames)
370
(set-window-type w 'toolbar)))
372
(define-wm-spec-window-type '_NET_WM_WINDOW_TYPE_MENU
374
(require 'sawfish.wm.frames)
375
(set-window-type w 'menu)))
377
(define-wm-spec-window-type '_NET_WM_WINDOW_TYPE_SPLASH
379
(require 'sawfish.wm.frames)
380
(set-window-type w 'splash)
381
(window-put w 'place-mode 'centered)))
383
(define-wm-spec-window-state '_NET_WM_STATE_STICKY
310
386
((init) (window-put w 'sticky-viewport t))
326
402
((remove) (unmaximize-window w direction))
327
403
((add) (maximize-window w direction))
328
404
((toggle) (maximize-window-toggle w direction))
329
((get) (case direction
330
((vertical) (window-maximized-vertically-p w))
331
((horizontal) (window-maximized-horizontally-p w))
332
(t (window-maximized-p w)))))))
405
((get) (if (window-maximized-fullscreen-p w)
408
((vertical) (window-maximized-vertically-p w))
409
((horizontal) (window-maximized-horizontally-p w))
410
(t (window-maximized-p w))))))))
334
412
(define-wm-spec-window-state '_NET_WM_STATE_MAXIMIZED_VERT
335
413
(wm-spec-maximize-handler 'vertical))
350
427
((toggle) (toggle-window-shaded w))
351
428
((get) (window-get w 'shaded)))))
353
(define-wm-spec-window-state
354
'_NET_WM_STATE_SKIP_PAGER
430
(define-wm-spec-window-state '_NET_WM_STATE_SKIP_PAGER
357
433
((init add) (window-put w 'window-list-skip t))
360
436
(not (window-get w 'window-list-skip))))
361
437
((get) (window-get w 'window-list-skip)))))
439
(define-wm-spec-window-state '_NET_WM_STATE_SKIP_TASKBAR
442
((init add) (window-put w 'task-list-skip t))
443
((remove) (window-put w 'task-list-skip nil))
444
((toggle) (window-put w 'task-list-skip
445
(not (window-get w 'task-list-skip))))
446
((get) (window-get w 'task-list-skip)))))
448
(define-wm-spec-window-state '_NET_WM_STATE_FULLSCREEN
450
(require 'sawfish.wm.state.maximize)
452
((init) (window-put w 'queued-fullscreen-maximize t))
453
((add remove) (maximize-window-fullscreen w (eq mode 'add)))
454
((toggle) (maximize-window-fullscreen-toggle w))
455
((get) (window-maximized-fullscreen-p w)))))
457
(define (above-below-handler depth w mode)
458
(require 'sawfish.wm.stacking)
461
(window-put w 'depth depth))
463
(set-window-depth w (if (eq mode 'add) depth 0)))
465
(set-window-depth w (if (= (window-depth w) depth) 0 depth)))
467
(= (window-depth w) depth))))
469
(define-wm-spec-window-state '_NET_WM_STATE_BELOW
471
(above-below-handler wm-spec-below-depth w mode)))
473
(define-wm-spec-window-state '_NET_WM_STATE_ABOVE
475
(above-below-handler wm-spec-above-depth w mode)))
364
478
;;; client messages
370
484
(when (windowp w)
371
485
(delete-window w)))
487
((_NET_SHOWING_DESKTOP)
488
(if (= (aref data 0) 1)
373
492
((_NET_WM_MOVERESIZE)
374
493
(when (and (windowp w) (window-mapped-p w))
375
494
(require 'sawfish.wm.commands.move-resize)
376
495
(let ((mode (aref data 2)))
377
(if (eq mode _NET_WM_MOVERESIZE_MOVE)
496
;; don't want grabs failing, sigh
497
(x-server-timestamp t t)
498
(if (or (eq mode _NET_WM_MOVERESIZE_MOVE)
499
(eq mode _NET_WM_MOVERESIZE_MOVE_KEYBOARD))
378
500
(move-window-interactively w)
379
501
(let ((move-resize-moving-edges
380
502
(cond ((eq mode _NET_WM_MOVERESIZE_SIZE_TOPLEFT) '(top left))
387
509
((eq mode _NET_WM_MOVERESIZE_SIZE_RIGHT) '(right)))))
388
510
(resize-window-interactively w))))))
390
((_NET_NUMBER_OF_DESKTOPS _NET_DESKTOP_GEOMETRY)
391
;; XXX these conflict with user preferences
512
((_NET_NUMBER_OF_DESKTOPS)
513
(set-number-of-workspaces (aref data 0)))
515
((_NET_DESKTOP_GEOMETRY)
516
(set-number-of-viewports (aref data 0) (aref data 1)))
394
518
((_NET_DESKTOP_VIEWPORT)
395
519
(set-viewport (aref data 0) (aref data 1)))
470
598
(vector wm-spec-window-id) 'WINDOW 32)
471
599
(set-x-property wm-spec-window-id '_NET_SUPPORTING_WM_CHECK
472
600
(vector wm-spec-window-id) 'WINDOW 32)
473
(set-x-property wm-spec-window-id '_NET_WM_NAME "Sawfish" 'STRING 8)
601
(set-x-property wm-spec-window-id '_NET_WM_NAME "Sawfish" 'UTF8_STRING 8)
475
(set-x-property 'root '_NET_SUPPORTED supported-protocols 'ATOM 32)
603
(set-x-property 'root '_NET_SUPPORTED supported-atoms 'ATOM 32)
477
605
(let ((current-desktop (get-x-property 'root '_NET_CURRENT_DESKTOP)))
478
606
(when (and current-desktop
490
618
(add-hook 'workspace-state-change-hook update-workspace-hints)
491
619
(add-hook 'viewport-resized-hook update-workspace-hints)
492
620
(add-hook 'viewport-moved-hook update-workspace-hints)
621
(add-hook 'workarea-changed-hook update-workspace-hints)
494
623
(add-hook 'add-window-hook update-client-list-hints)
495
624
(add-hook 'destroy-notify-hook update-client-list-hints)
500
629
(add-hook 'before-add-window-hook honour-client-state)
501
630
(add-hook 'add-window-hook update-client-state)
502
(call-after-state-changed '(sticky shaded maximized stacking)
631
(call-after-state-changed '(sticky shaded maximized stacking
632
window-list-skip task-list-skip)
503
633
update-client-state)
634
(call-after-state-changed 'sticky update-window-workspace-hints)
505
636
(add-hook 'focus-in-hook update-focus-state)
506
637
(add-hook 'focus-out-hook update-focus-state)