/*
* lwm, a window manager for X11
* Copyright (C) 1997-2003 Elliott Hughes, James Carter
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "lwm.h"
#include "ewmh.h"
Atom ewmh_atom[EWMH_ATOM_LAST];
Atom utf8_string;
void
ewmh_init(void) {
/* build half a million EWMH atoms */
ewmh_atom[_NET_SUPPORTED] =
XInternAtom(dpy, "_NET_SUPPORTED", False);
ewmh_atom[_NET_CLIENT_LIST] =
XInternAtom(dpy, "_NET_CLIENT_LIST", False);
ewmh_atom[_NET_CLIENT_LIST_STACKING] =
XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False);
ewmh_atom[_NET_NUMBER_OF_DESKTOPS] =
XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False);
ewmh_atom[_NET_DESKTOP_GEOMETRY] =
XInternAtom(dpy, "_NET_DESKTOP_GEOMETRY", False);
ewmh_atom[_NET_DESKTOP_VIEWPORT] =
XInternAtom(dpy, "_NET_DESKTOP_VIEWPORT", False);
ewmh_atom[_NET_CURRENT_DESKTOP] =
XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False);
ewmh_atom[_NET_ACTIVE_WINDOW] =
XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
ewmh_atom[_NET_WORKAREA] =
XInternAtom(dpy, "_NET_WORKAREA", False);
ewmh_atom[_NET_SUPPORTING_WM_CHECK] =
XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
ewmh_atom[_NET_CLOSE_WINDOW] =
XInternAtom(dpy, "_NET_CLOSE_WINDOW", False);
ewmh_atom[_NET_MOVERESIZE_WINDOW] =
XInternAtom(dpy, "_NET_MOVERESIZE_WINDOW", False);
ewmh_atom[_NET_WM_MOVERESIZE] =
XInternAtom(dpy, "_NET_WM_MOVERESIZE", False);
ewmh_atom[_NET_WM_NAME] =
XInternAtom(dpy, "_NET_WM_NAME", False);
ewmh_atom[_NET_WM_WINDOW_TYPE] =
XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
ewmh_atom[_NET_WM_STATE] =
XInternAtom(dpy, "_NET_WM_STATE", False);
ewmh_atom[_NET_WM_ALLOWED_ACTIONS] =
XInternAtom(dpy, "_NET_WM_ALLOWED_ACTIONS", False);
ewmh_atom[_NET_WM_STRUT] =
XInternAtom(dpy, "_NET_WM_STRUT", False);
ewmh_atom[_NET_WM_WINDOW_TYPE_DESKTOP] =
XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
ewmh_atom[_NET_WM_WINDOW_TYPE_DOCK] =
XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
ewmh_atom[_NET_WM_WINDOW_TYPE_TOOLBAR] =
XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
ewmh_atom[_NET_WM_WINDOW_TYPE_MENU] =
XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_MENU", False);
ewmh_atom[_NET_WM_WINDOW_TYPE_UTILITY] =
XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False);
ewmh_atom[_NET_WM_WINDOW_TYPE_SPLASH] =
XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False);
ewmh_atom[_NET_WM_WINDOW_TYPE_DIALOG] =
XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
ewmh_atom[_NET_WM_WINDOW_TYPE_NORMAL] =
XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False);
ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR] =
XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False);
ewmh_atom[_NET_WM_STATE_SKIP_PAGER] =
XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", False);
ewmh_atom[_NET_WM_STATE_HIDDEN] =
XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False);
ewmh_atom[_NET_WM_STATE_FULLSCREEN] =
XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
ewmh_atom[_NET_WM_ACTION_MOVE] =
XInternAtom(dpy, "_NET_WM_ACTION_MOVE", False);
ewmh_atom[_NET_WM_ACTION_RESIZE] =
XInternAtom(dpy, "_NET_WM_ACTION_RESIZE", False);
ewmh_atom[_NET_WM_ACTION_FULLSCREEN] =
XInternAtom(dpy, "_NET_WM_ACTION_FULLSCREEN", False);
ewmh_atom[_NET_WM_ACTION_CLOSE] =
XInternAtom(dpy, "_NET_WM_ACTION_CLOSE", False);
utf8_string = XInternAtom(dpy, "UTF8_STRING", False);
}
void
ewmh_init_screens(void) {
int i;
unsigned long data[4];
/* announce EWMH compatibility on all acreens */
for (i = 0; i < screen_count; i++) {
screens[i].ewmh_set_client_list = False;
screens[i].ewmh_compat = XCreateSimpleWindow(dpy,
screens[i].root,
-200, -200, 1, 1,
0, 0, 0);
XChangeProperty(dpy, screens[i].ewmh_compat,
ewmh_atom[_NET_WM_NAME],
utf8_string, 8, PropModeReplace,
"lwm", 3);
/* set root window properties */
XChangeProperty(dpy, screens[i].root,
ewmh_atom[_NET_SUPPORTED],
XA_ATOM, 32, PropModeReplace,
(unsigned char *)ewmh_atom, EWMH_ATOM_LAST);
XChangeProperty(dpy, screens[i].root,
ewmh_atom[_NET_SUPPORTING_WM_CHECK],
XA_WINDOW, 32, PropModeReplace,
(unsigned char *)&screens[i].ewmh_compat, 1);
data[0] = 1;
XChangeProperty(dpy, screens[i].root,
ewmh_atom[_NET_NUMBER_OF_DESKTOPS],
XA_CARDINAL, 32, PropModeReplace,
(unsigned char*)data, 1);
data[0] = screens[i].display_width;
data[1] = screens[i].display_height;
XChangeProperty(dpy, screens[i].root,
ewmh_atom[_NET_DESKTOP_GEOMETRY],
XA_CARDINAL, 32, PropModeReplace,
(unsigned char*)data, 2);
data[0] = 0;
data[1] = 0;
XChangeProperty(dpy, screens[i].root,
ewmh_atom[_NET_DESKTOP_VIEWPORT],
XA_CARDINAL, 32, PropModeReplace,
(unsigned char*)data, 2);
data[0] = 0;
XChangeProperty(dpy, screens[i].root,
ewmh_atom[_NET_CURRENT_DESKTOP],
XA_CARDINAL, 32, PropModeReplace,
(unsigned char*)data, 1);
ewmh_set_strut(&screens[i]);
ewmh_set_client_list(&screens[i]);
}
}
EWMHWindowType
ewmh_get_window_type(Window w) {
Atom rt;
Atom *type;
int fmt;
unsigned long n;
unsigned long extra;
int i;
EWMHWindowType ret;
i = XGetWindowProperty(dpy, w,
ewmh_atom[_NET_WM_WINDOW_TYPE],
0, 100, False, XA_ATOM, &rt, &fmt, &n, &extra,
(unsigned char **)&type);
if (i != Success || type == NULL)
return WTypeNone;
ret = WTypeNone;
for (; n; n--) {
if (type[n - 1] ==
ewmh_atom[_NET_WM_WINDOW_TYPE_DESKTOP]) {
ret = WTypeDesktop;
break;
}
if (type[n - 1] ==
ewmh_atom[_NET_WM_WINDOW_TYPE_DOCK]) {
ret = WTypeDock;
break;
}
if (type[n - 1] ==
ewmh_atom[_NET_WM_WINDOW_TYPE_TOOLBAR]) {
ret = WTypeToolbar;
break;
}
if (type[n - 1] ==
ewmh_atom[_NET_WM_WINDOW_TYPE_MENU]) {
ret = WTypeMenu;
break;
}
if (type[n - 1] ==
ewmh_atom[_NET_WM_WINDOW_TYPE_UTILITY]) {
ret = WTypeUtility;
break;
}
if (type[n - 1] ==
ewmh_atom[_NET_WM_WINDOW_TYPE_SPLASH]) {
ret = WTypeSplash;
break;
}
if (type[n - 1] ==
ewmh_atom[_NET_WM_WINDOW_TYPE_DIALOG]) {
ret = WTypeDialog;
break;
}
if (type[n - 1] ==
ewmh_atom[_NET_WM_WINDOW_TYPE_NORMAL]) {
ret = WTypeNormal;
break;
}
}
XFree(type);
return ret;
}
Bool ewmh_get_window_name(Client *c) {
#ifdef X_HAVE_UTF8_STRING
Atom rt;
char *name;
int fmt;
unsigned long n;
unsigned long extra;
int i;
i = XGetWindowProperty(dpy, c->window,
ewmh_atom[_NET_WM_NAME],
0, 100, False, utf8_string, &rt, &fmt, &n, &extra,
(unsigned char **)&name);
if (i != Success || name == NULL)
return False;
Client_Name(c, name, True);
XFree(name);
return True;
#else
return False;
#endif
}
Bool
ewmh_hasframe(Client *c) {
switch (c->wtype) {
case WTypeDesktop:
case WTypeDock:
case WTypeMenu:
case WTypeSplash:
return False;
default:
return True;
}
}
void
ewmh_get_state(Client *c) {
Atom rt;
Atom *state;
int fmt;
unsigned long n;
unsigned long extra;
int i;
if (c == NULL) return;
i = XGetWindowProperty(dpy, c->window,
ewmh_atom[_NET_WM_STATE],
0, 100, False, XA_ATOM, &rt, &fmt, &n, &extra,
(unsigned char **)&state);
if (i != Success || state == NULL) return;
c->wstate.skip_taskbar = False;
c->wstate.skip_pager = False;
c->wstate.fullscreen = False;
c->wstate.above = False;
c->wstate.below = False;
for (; n; n--) {
if (state[n - 1] ==
ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR])
c->wstate.skip_taskbar = True;
if (state[n - 1] ==
ewmh_atom[_NET_WM_STATE_SKIP_PAGER])
c->wstate.skip_pager = True;
if (state[n - 1] ==
ewmh_atom[_NET_WM_STATE_FULLSCREEN])
c->wstate.fullscreen = True;
if (state[n - 1] ==
ewmh_atom[_NET_WM_STATE_ABOVE])
c->wstate.above = True;
if (state[n - 1] ==
ewmh_atom[_NET_WM_STATE_BELOW])
c->wstate.below = True;
}
XFree(state);
}
static Bool
new_state(unsigned long action, Bool current)
{
enum Action {remove, add, toggle};
switch (action) {
case remove:
return False;
case add:
return True;
case toggle:
if (current == True) return False; else return True;
}
fprintf(stderr,"%s: bad action in _NET_WM_STATE (%d)\n",
argv0, (int) action);
return current;
}
void
ewmh_change_state(Client *c, unsigned long action,
unsigned long atom) {
Atom *a = (Atom *)&atom;
if (atom == 0) return;
if (*a == ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR])
c->wstate.skip_taskbar =
new_state(action, c->wstate.skip_taskbar);
if (*a == ewmh_atom[_NET_WM_STATE_SKIP_PAGER])
c->wstate.skip_pager =
new_state(action, c->wstate.skip_pager);
if (*a == ewmh_atom[_NET_WM_STATE_FULLSCREEN]) {
Bool was_fullscreen = c->wstate.fullscreen;
c->wstate.fullscreen =
new_state(action, c->wstate.fullscreen);
if (was_fullscreen == False &&
c->wstate.fullscreen == True) Client_EnterFullScreen(c);
if (was_fullscreen == True &&
c->wstate.fullscreen == False) Client_ExitFullScreen(c);
}
if (*a == ewmh_atom[_NET_WM_STATE_ABOVE])
c->wstate.above =
new_state(action, c->wstate.above);
if (*a == ewmh_atom[_NET_WM_STATE_BELOW])
c->wstate.below =
new_state(action, c->wstate.below);
ewmh_set_state(c);
/* may have to shuffle windows in the stack after a change of state */
ewmh_set_client_list(c->screen);
}
void
ewmh_set_state(Client *c) {
int atoms = 0;
Atom *a = NULL;
int i = 0;
if (c == NULL) return;
if (c->state != WithdrawnState) {
if (c->hidden == True) atoms++;
if (c->wstate.skip_taskbar == True) atoms++;
if (c->wstate.skip_pager == True) atoms++;
if (c->wstate.fullscreen == True) atoms++;
if (c->wstate.above == True) atoms++;
if (c->wstate.below == True) atoms++;
if (atoms > 0) a = malloc(sizeof(Atom) * atoms);
if (c->hidden == True) {
a[i] = ewmh_atom[_NET_WM_STATE_HIDDEN];
i++;
}
if (c->wstate.skip_taskbar == True) {
a[i] = ewmh_atom[_NET_WM_STATE_SKIP_TASKBAR];
i++;
}
if (c->wstate.skip_pager == True) {
a[i] = ewmh_atom[_NET_WM_STATE_SKIP_PAGER];
i++;
}
if (c->wstate.fullscreen == True) {
a[i] = ewmh_atom[_NET_WM_STATE_FULLSCREEN];
i++;
}
if (c->wstate.above == True) {
a[i] = ewmh_atom[_NET_WM_STATE_ABOVE];
i++;
}
if (c->wstate.below == True) {
a[i] = ewmh_atom[_NET_WM_STATE_BELOW];
i++;
}
}
XChangeProperty(dpy, c->window, ewmh_atom[_NET_WM_STATE],
XA_ATOM, 32, PropModeReplace, (unsigned char *)a, atoms);
if (a != NULL) free(a);
}
void
ewmh_set_allowed(Client *c)
{
/* FIXME: this is dumb - the allowed actions should be calculuated
* but for now, anything goes.
*/
Atom action[4];
action[0] = ewmh_atom[_NET_WM_ACTION_MOVE];
action[1] = ewmh_atom[_NET_WM_ACTION_RESIZE];
action[2] = ewmh_atom[_NET_WM_ACTION_FULLSCREEN];
action[3] = ewmh_atom[_NET_WM_ACTION_CLOSE];
XChangeProperty(dpy, c->window, ewmh_atom[_NET_WM_ALLOWED_ACTIONS],
XA_ATOM, 32, PropModeReplace, (unsigned char *)action, 4);
}
void
ewmh_set_strut(ScreenInfo *screen) {
Client *c;
EWMHStrut strut;
unsigned long data[4];
/* FIXME: add parameter to MakeSane rather than this hack */
Edge backup;
/* find largest reserved areas */
strut.left = 0;
strut.right = 0;
strut.top = 0;
strut.bottom = 0;
for (c = client_head(); c; c = c->next) {
if (c->screen != screen) continue;
if (c->strut.left > strut.left) strut.left = c->strut.left;
if (c->strut.right > strut.right) strut.right = c->strut.right;
if (c->strut.top > strut.top) strut.top = c->strut.top;
if (c->strut.bottom > strut.bottom)
strut.bottom = c->strut.bottom;
}
/* if the reservered aread have not changed then we're done */
if ( screen->strut.left == strut.left &&
screen->strut.right == strut.right &&
screen->strut.top == strut.top &&
screen->strut.bottom == strut.bottom) return;
/* apply the new strut */
screen->strut.left = strut.left;
screen->strut.right = strut.right;
screen->strut.top = strut.top;
screen->strut.bottom = strut.bottom;
/* set the new workarea */
data[0] = strut.left;
data[1] = strut.top;
data[2] = screen->display_width - (strut.left + strut.right);
data[3] = screen->display_height - (strut.top + strut.bottom);
XChangeProperty(dpy, screen->root,
ewmh_atom[_NET_WORKAREA],
XA_CARDINAL, 32, PropModeReplace,
(unsigned char*)data, 4);
/* ensure no window fully occupy reserved areas */
for (c = client_head(); c; c = c->next) {
int x = c->size.x;
int y = c->size.y;
if (c->wstate.fullscreen == True) continue;
backup = interacting_edge;
interacting_edge = ENone;
Client_MakeSane(c, ENone, &x, &y, 0, 0);
interacting_edge = backup;
if (c->framed == True) {
XMoveWindow(dpy, c->parent,
c->size.x,
c->size.y - titleHeight());
} else {
XMoveWindow(dpy, c->parent,
c->size.x, c->size.y);
}
sendConfigureNotify(c);
}
}
/*
* get _NET_WM_STRUT and if it is available recalulate the screens
* reserved areas. the EWMH spec isn't clear about what we should do
* about hidden windows. It seems silly to reserve space for an invisible
* window, but the spec allows it. Ho Hum... jfc
*/
void
ewmh_get_strut(Client *c) {
Atom rt;
unsigned long *strut;
int fmt;
unsigned long n;
unsigned long extra;
int i;
if (c == NULL) return;
i = XGetWindowProperty(dpy, c->window,
ewmh_atom[_NET_WM_STRUT],
0, 5, False, XA_CARDINAL, &rt, &fmt, &n, &extra,
(unsigned char **)&strut);
if (i != Success || strut == NULL || n < 4) return;
c->strut.left = (unsigned int) strut[0];
c->strut.right = (unsigned int) strut[1];
c->strut.top = (unsigned int) strut[2];
c->strut.bottom = (unsigned int) strut[3];
ewmh_set_strut(c->screen);
}
/* fix stack forces each window on the screen to be in the right place in
* the window stack as indicated in the EWMH spec version 1.2 (section 7.10).
*/
static void
fix_stack(ScreenInfo *screen) {
Client *c;
/* this is pretty dumb. we should query the tree and only move
* those windows that require it. doing it regardless liek this
* causes the desktop to flicker
*/
/* first lower clients with _NET_WM_STATE_BELOW */
for (c = client_head(); c; c = c->next) {
if (c->wstate.below == False) continue;
Client_Lower(c);
}
/* lower desktops - they are always the lowest */
for (c = client_head(); c; c = c->next) {
if (c->wtype != WTypeDesktop) continue;
Client_Lower(c);
break; /* only one desktop, surely */
}
/* raise clients with _NET_WM_STATE_ABOVE and docks
* (unless marked with _NET_WM_STATE_BELOW)
*/
for (c = client_head(); c; c = c->next) {
if (!(c->wstate.above == True ||
(c->wtype == WTypeDock &&
c->wstate.below == False)))
continue;
Client_Raise(c);
}
/* raise fullscreens - they're always on top */
for (c = client_head(); c; c = c->next) {
if (c->wstate.fullscreen == False) continue;
Client_Raise(c);
}
}
static Bool
valid_for_client_list(ScreenInfo *screen, Client *c) {
if (c->screen != screen) return False;
if (c->state == WithdrawnState) return False;
return True;
}
/*
* update_client_list updates the properties on the root window used by
* task lists and pagers.
*
* it should be called whenever the window stack is modified, or when clients
* are hidden or unhidden.
*/
void
ewmh_set_client_list(ScreenInfo *screen) {
int no_clients=0;
Window *client_list=NULL;
Window *stacked_client_list=NULL;
Client *c;
if (screen == NULL || screen->ewmh_set_client_list == True) return;
screen->ewmh_set_client_list = True;
fix_stack(screen);
for (c = client_head(); c; c = c->next) {
if (valid_for_client_list(screen, c) == True) no_clients++;
}
if (no_clients > 0) {
int i;
Window dw1;
Window dw2;
Window *wins;
unsigned int win;
unsigned int nwins;
client_list = malloc(sizeof(Window) * no_clients);
i = no_clients - 1; /* array starts with oldest */
for (c = client_head(); c; c = c->next) {
if (valid_for_client_list(screen, c) == True) {
client_list[i] = c->window;
i--;
if (i < 0) break;
}
}
stacked_client_list = malloc(sizeof(Window) * no_clients);
i = 0;
XQueryTree(dpy, screen->root, &dw1, &dw2, &wins, &nwins);
for (win = 0; win < nwins; win++) {
c = Client_Get(wins[win]);
if (!c) continue;
if (valid_for_client_list(screen, c) == True) {
stacked_client_list[i] = c->window;
i++;
if (i >= no_clients) break;
}
}
if ( nwins > 0 ) XFree(wins);
}
XChangeProperty(dpy, screen->root,
ewmh_atom[_NET_CLIENT_LIST],
XA_WINDOW, 32, PropModeReplace,
(unsigned char*)client_list, no_clients);
XChangeProperty(dpy, screen->root,
ewmh_atom[_NET_CLIENT_LIST_STACKING],
XA_WINDOW, 32, PropModeReplace,
(unsigned char*)stacked_client_list, no_clients);
if (no_clients > 0 ) {
free(client_list);
free(stacked_client_list);
}
screen->ewmh_set_client_list = False;
}
syntax highlighted by Code2HTML, v. 0.9.1