-
Notifications
You must be signed in to change notification settings - Fork 4
/
ewmh.c
237 lines (203 loc) · 7.76 KB
/
ewmh.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/* evilwm - minimalist window manager for X11
* Copyright (C) 1999-2022 Ciaran Anscomb <[email protected]>
* see README for license and other details. */
// Extended Window Manager Hints
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <unistd.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include "client.h"
#include "display.h"
#include "ewmh.h"
#include "list.h"
#include "log.h"
#include "screen.h"
#include "util.h"
// Maintain a reasonably sized allocated block of memory for lists
// of windows (for feeding to XChangeProperty in one hit).
static Window *window_array = NULL;
static Window *alloc_window_array(void);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Update various properties that reflect the screen geometry.
void ewmh_set_screen_workarea(struct screen *s) {
unsigned long workarea[4] = {
0, 0,
DisplayWidth(display.dpy, s->screen), DisplayHeight(display.dpy, s->screen)
};
XChangeProperty(display.dpy, s->root, X_ATOM(_NET_DESKTOP_GEOMETRY),
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&workarea[2], 2);
XChangeProperty(display.dpy, s->root, X_ATOM(_NET_DESKTOP_VIEWPORT),
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&workarea[0], 2);
XChangeProperty(display.dpy, s->root, X_ATOM(_NET_WORKAREA),
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&workarea, 4);
}
// Update the _NET_CLIENT_LIST property for a screen. This is a simple list of
// all client windows in the order they were mapped.
void ewmh_set_net_client_list(struct screen *s) {
Window *windows = alloc_window_array();
int i = 0;
if (windows) {
for (struct list *iter = clients_mapping_order; iter; iter = iter->next) {
struct client *c = iter->data;
if (c->screen == s) {
windows[i++] = c->window;
}
}
}
XChangeProperty(display.dpy, s->root, X_ATOM(_NET_CLIENT_LIST),
XA_WINDOW, 32, PropModeReplace,
(unsigned char *)windows, i);
}
// Update the _NET_CLIENT_LIST_STACKING property for a screen. Similar to
// _NET_CLIENT_LIST, but in stacking order (bottom to top).
void ewmh_set_net_client_list_stacking(struct screen *s) {
Window *windows = alloc_window_array();
int i = 0;
if (windows) {
for (struct list *iter = clients_stacking_order; iter; iter = iter->next) {
struct client *c = iter->data;
if (c->screen == s) {
windows[i++] = c->window;
}
}
}
XChangeProperty(display.dpy, s->root, X_ATOM(_NET_CLIENT_LIST_STACKING),
XA_WINDOW, 32, PropModeReplace,
(unsigned char *)windows, i);
}
// Update _NET_CURRENT_DESKTOP for screen to currently selected vdesk.
void ewmh_set_net_current_desktop(struct screen *s) {
unsigned long vdesk = s->vdesk;
XChangeProperty(display.dpy, s->root, X_ATOM(_NET_CURRENT_DESKTOP),
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&vdesk, 1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Set the _NET_WM_ALLOWED_ACTIONS on a client advertising what we support.
void ewmh_set_allowed_actions(struct client *c) {
Atom allowed_actions[] = {
X_ATOM(_NET_WM_ACTION_MOVE),
X_ATOM(_NET_WM_ACTION_MAXIMIZE_HORZ),
X_ATOM(_NET_WM_ACTION_MAXIMIZE_VERT),
X_ATOM(_NET_WM_ACTION_FULLSCREEN),
X_ATOM(_NET_WM_ACTION_CHANGE_DESKTOP),
X_ATOM(_NET_WM_ACTION_CLOSE),
// nelements reduced to omit this if not possible:
X_ATOM(_NET_WM_ACTION_RESIZE),
};
int nelements = sizeof(allowed_actions) / sizeof(Atom);
// Omit resize element if resizing not possible:
if (c->max_width && c->max_width == c->min_width
&& c->max_height && c->max_height == c->min_height)
nelements--;
XChangeProperty(display.dpy, c->window, X_ATOM(_NET_WM_ALLOWED_ACTIONS),
XA_ATOM, 32, PropModeReplace,
(unsigned char *)&allowed_actions,
nelements);
// As this function is only called when creating a client, take this
// opportunity to set any initial state on its window. In particular,
// I'm interested in docks immediately getting focussed state.
ewmh_set_net_wm_state(c);
}
// When window manager is shutting down, the _NET_WM_ALLOWED_ACTIONS property
// is removed from all clients (as no WM now exists to service these actions).
void ewmh_remove_allowed_actions(struct client *c) {
XDeleteProperty(display.dpy, c->window, X_ATOM(_NET_WM_ALLOWED_ACTIONS));
}
// These properties are removed when a window is "withdrawn".
void ewmh_withdraw_client(struct client *c) {
XDeleteProperty(display.dpy, c->window, X_ATOM(_NET_WM_DESKTOP));
XDeleteProperty(display.dpy, c->window, X_ATOM(_NET_WM_STATE));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Update _NET_WM_DESKTOP to reflect virtual desktop of client (including
// fixed, 0xffffffff).
void ewmh_set_net_wm_desktop(struct client *c) {
unsigned long vdesk = c->vdesk;
XChangeProperty(display.dpy, c->window, X_ATOM(_NET_WM_DESKTOP),
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&vdesk, 1);
}
// Check _NET_WM_WINDOW_TYPE property and build a bitmask of EWMH_WINDOW_TYPE_*
unsigned ewmh_get_net_wm_window_type(Window w) {
Atom *aprop;
unsigned long nitems, i;
unsigned type = 0;
if ( (aprop = get_property(w, X_ATOM(_NET_WM_WINDOW_TYPE), XA_ATOM, &nitems)) ) {
for (i = 0; i < nitems; i++) {
if (aprop[i] == X_ATOM(_NET_WM_WINDOW_TYPE_DESKTOP))
type |= EWMH_WINDOW_TYPE_DESKTOP;
if (aprop[i] == X_ATOM(_NET_WM_WINDOW_TYPE_DOCK))
type |= EWMH_WINDOW_TYPE_DOCK;
if (aprop[i] == X_ATOM(_NET_WM_WINDOW_TYPE_NOTIFICATION))
type |= EWMH_WINDOW_TYPE_NOTIFICATION;
}
XFree(aprop);
}
return type;
}
// Update _NET_WM_STATE_* properties on a window. Also updates
// _NET_ACTIVE_WINDOW on the client's screen if necessary.
void ewmh_set_net_wm_state(struct client *c) {
Atom state[4];
int i = 0;
if (c->oldh)
state[i++] = X_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
if (c->oldw)
state[i++] = X_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
if (c->oldh && c->oldw)
state[i++] = X_ATOM(_NET_WM_STATE_FULLSCREEN);
if (c == current || c->is_dock)
state[i++] = X_ATOM(_NET_WM_STATE_FOCUSED);
if (c == current) {
if (c->screen->active != c->window) {
XChangeProperty(display.dpy, c->screen->root,
X_ATOM(_NET_ACTIVE_WINDOW),
XA_WINDOW, 32, PropModeReplace,
(unsigned char *)&c->window, 1);
c->screen->active = c->window;
}
} else if (c->screen->active == c->window) {
Window w = None;
XChangeProperty(display.dpy, c->screen->root, X_ATOM(_NET_ACTIVE_WINDOW),
XA_WINDOW, 32, PropModeReplace,
(unsigned char *)&w, 1);
c->screen->active = None;
}
XChangeProperty(display.dpy, c->window, X_ATOM(_NET_WM_STATE),
XA_ATOM, 32, PropModeReplace,
(unsigned char *)&state, i);
}
// When we receive _NET_REQUEST_FRAME_EXTENTS from an unmapped window, we are
// to set _NET_FRAME_EXTENTS on that window even before it becomes managed.
// TODO: set this property on a client once the border size for it is known
// during client_manage_new().
void ewmh_set_net_frame_extents(Window w, unsigned long border) {
unsigned long extents[4];
extents[0] = extents[1] = extents[2] = extents[3] = border;
XChangeProperty(display.dpy, w, X_ATOM(_NET_FRAME_EXTENTS),
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&extents, 4);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Allocate/resize an array suitable to hold all client window ids.
//
// XXX should test that this can be allocated before we commit to managing a
// window, in the same way that we test the client structure allocation.
static Window *alloc_window_array(void) {
unsigned count = 0;
for (struct list *iter = clients_mapping_order; iter; iter = iter->next) {
count++;
}
if (count == 0) count++;
// Round up to next block of 128
count = (count + 127) & ~127;
window_array = realloc(window_array, count * sizeof(Window));
return window_array;
}