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 <evilwm@6809.org.uk>
* 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;
}
|