Generic_Window_Manager/icccm.c

696 lines
18 KiB
C

/* Copyright 1989 GROUPE BULL -- See license conditions in file COPYRIGHT
* Copyright 1989 Massachusetts Institute of Technology
*/
/***********************************************\
* *
* GWM: icccm.c *
* Miscellaneous utilities to enforce the ICCCM *
* *
\***********************************************/
#include "EXTERN.h"
#include <stdio.h>
#include "wool.h"
#include "wl_atom.h"
#include "wl_list.h"
#include "wl_string.h"
#include "gwm.h"
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include "wl_number.h"
extern XError(), NoXError();
DECLARE_strchr;
/* Used to tell a client that it has been moved
*/
SendSyntheticMoveEvent(cw)
ClientWindow cw;
{
XConfigureEvent event;
if(cw -> client_wob) /* do not send to ourselves */
return;
event.type = ConfigureNotify;
event.display = dpy;
event.event = cw -> client;
event.window = cw -> client;
event.x = cw -> inner_x + cw ->box.x + cw -> box.borderwidth;
event.y = cw -> inner_y + cw ->box.y + cw -> box.borderwidth;
event.width = cw -> inner_width;
event.height = cw -> inner_height;
event.border_width = cw -> inner_borderwidth;
event.above = cw -> hook;
event.override_redirect = False;
Trace('e', ("SendSyntheticMoveEvent: event 0x%x window 0x%x x %d y %d w %d h %d bw %d above 0x%x\n", event.event, event.window, event.x, event.y, event.width, event.height, event.border_width, event.above));
XSendEvent(dpy, cw -> client, False, StructureNotifyMask,
(XEvent *) &event);
}
/* for defining another icon for WM_STATE property
*/
WOOL_OBJECT
wool_wm_state_user_icon_get()
{
if (TargetWindow -> cached_props -> user_icon)
return (WOOL_OBJECT)
WLNumber_make(TargetWindow -> cached_props -> user_icon);
else
return NIL;
}
WOOL_OBJECT
wool_wm_state_user_icon_set(number)
WOOL_Number number;
{
must_be_number(number, 0);
TargetWindow -> cached_props -> user_icon = (ClientWindow)
number -> number;
Update_XA_WM_STATE(TargetWindow);
return (WOOL_OBJECT) number;
}
/* updating this window's WM_STATE once another icon is defined
*/
WOOL_OBJECT
wool_wm_state_update(argc, argv)
int argc;
WOOL_Atom argv[];
{
int state = -1;
if (argc) {
if (argv[0] == WA_window)
state = WM_STATE_Normal;
else if (argv[0] == WA_icon)
state = WM_STATE_Iconified;
else if ((WOOL_OBJECT) argv[0] == NIL)
state = WM_STATE_Withdrawn;
}
if (!TargetWindow -> window -> client_wob)
Set_XA_WM_STATE(TargetWindow -> window, state);
return NIL;
}
/*&WN window-wm-state
* getting this window's WM_STATE once another icon is defined
*/
WOOL_OBJECT
wool_wm_state_get(argc, argv)
int argc;
WOOL_Number argv[];
{
ClientWindow cw = TargetWindow;
Atom actualtype;
int actualformat;
unsigned long nitems;
unsigned long bytesafter;
WM_STATE_PROP wm_state;
if (argc) {
must_be_number(argv[0], 0);
cw = (ClientWindow) argv[0] -> number;
}
cw = cw -> window;
if ((Success == XGetWindowProperty(dpy, cw -> client, XA_WM_STATE, 0,
WM_STATE_PROP_Length, False, XA_WM_STATE, &actualtype,
&actualformat, &nitems, &bytesafter,
(unsigned char **) &wm_state))
&& nitems && wm_state) {
switch (wm_state -> state) {
case WM_STATE_Iconified:
XFree(wm_state);
return (WOOL_OBJECT) WA_icon;
case WM_STATE_Normal:
XFree(wm_state);
return (WOOL_OBJECT) WA_window;
default:
XFree(wm_state);
}
}
return NIL;
}
/* Colormaps
* colormap focus management:
*/
WOOL_OBJECT
wool_set_colormap_focus(argc, argv)
int argc;
WOOL_Number argv[];
{
ClientWindow cw = TargetWindow;
if (argc) {
if (argv[0] == (WOOL_Number) NIL) {
GWM_set_default_colormap();
Context -> InstalledColormapCW = Context -> rootWob;
return NIL;
}
must_be_number(argv[0], 0);
cw = (ClientWindow) argv[0] -> number;
}
if (cw -> colormap) {
if (cw != Context -> InstalledColormapCW &&
(!Context -> InstalledColormapCW ||
cw -> colormap != Context -> InstalledColormapCW -> colormap))
XInstallColormap(dpy, cw -> colormap);
if (cw -> cached_props -> colormap_windows)
cw -> cached_props -> colormap_windows_index = 0;
} else {
GWM_set_default_colormap();
}
Context -> InstalledColormapCW = cw;
return NIL;
}
GWM_set_default_colormap()
{
if (Context -> InstalledColormapCW != Context -> rootWob &&
(!Context -> InstalledColormapCW ||
(Context -> InstalledColormapCW -> colormap &&
Context -> InstalledColormapCW -> colormap
!= Context -> rootWob -> colormap))) {
XInstallColormap(dpy, Context -> rootWob -> colormap);
Context -> InstalledColormapCW = Context -> rootWob;
}
}
/* Sub windows colormaps
* colormap focus management:
*/
WOOL_OBJECT
wool_set_subwindow_colormap_focus(argc, argv)
int argc;
WOOL_Number argv[];
{
ClientWindow cw = TargetWindow;
int index;
XWindowAttributes wa;
Colormap current_colormap;
if (argc) {
must_be_number(argv[0], 0);
index = argv[0] -> number;
}
if (cw == Context -> InstalledColormapCW &&
cw -> cached_props -> colormap_windows) {
if (argc) { /* absolute index */
cw -> cached_props -> colormap_windows_index =
index % cw -> cached_props -> colormap_windows_size;
XGetWindowAttributes(dpy,
cw -> cached_props -> colormap_windows[
cw -> cached_props -> colormap_windows_index], &wa);
if (wa.colormap)
XInstallColormap(dpy, wa.colormap);
} else { /* increment index */
XGetWindowAttributes(dpy,
cw -> cached_props -> colormap_windows[
cw -> cached_props -> colormap_windows_index], &wa);
current_colormap = wa.colormap;
for (index = cw -> cached_props -> colormap_windows_index + 1;
index % cw -> cached_props -> colormap_windows_size !=
cw -> cached_props -> colormap_windows_index;
index++) {
index = index % cw -> cached_props -> colormap_windows_size;
XGetWindowAttributes(dpy,
cw -> cached_props -> colormap_windows[index], &wa);
if (wa.colormap != current_colormap) {
cw -> cached_props -> colormap_windows_index = index;
XInstallColormap(dpy, wa.colormap);
break;
}
}
}
}
return NIL;
}
/* updating a colormap for a watched window in a WM_COLORMAP_WINDOWS list
*/
Update_colormap_in_colormap_windows_list(evt)
XColormapEvent *evt;
{
ClientWindow cw;
if (ContextOfXWindow(evt -> window))
return; /* if no more window, returns */
cw = Context -> InstalledColormapCW;
if (cw && cw -> cached_props -> colormap_windows_index &&
evt -> window == cw -> cached_props -> colormap_windows[
cw -> cached_props -> colormap_windows_index]
&& evt -> new)
if (evt -> colormap)
XInstallColormap(dpy, evt -> colormap);
else
XInstallColormap(dpy, Context->rootWob -> colormap);
}
/* declare accepted icons sizes for future clients
*/
WOOL_OBJECT
wool_set_wm_icon_sizes(argc, argv)
int argc;
WOOL_Number argv[];
{
int i;
XIconSize icon_sizes;
if(argc != 6)
return wool_error(BAD_NUMBER_OF_ARGS, argc);
for(i=0;i<6;i++){
must_be_number(argv[i], i);
}
icon_sizes.min_width = argv[0] -> number;
icon_sizes.min_height = argv[1] -> number;
icon_sizes.max_width = argv[2] -> number;
icon_sizes.max_height = argv[3] -> number;
icon_sizes.width_inc = argv[4] -> number;
icon_sizes.height_inc = argv[5] -> number;
XSetIconSizes(dpy, Context->root, &icon_sizes, 6);
return NIL;
}
/* get WM_COMMAND
*/
WOOL_OBJECT
wool_get_wm_command()
{
Atom actual_type;
int actual_format = 0;
unsigned long nitems = 0L, leftover = 0L;
unsigned char *prop = NULL;
WOOL_List list;
int i, nstrings = 0, n;
unsigned char *p;
if (XGetWindowProperty (dpy, TargetWindow -> client, XA_WM_COMMAND,
0L, 1000000L, False,
AnyPropertyType, &actual_type, &actual_format,
&nitems, &leftover, &prop) == Success &&
actual_type != None && prop && nitems) {
for (i = 0; i < nitems; i++)
if (prop[i] == '\0')
nstrings ++;
list = wool_list_make(nstrings);
p = prop;
for (n = 0; n < nstrings; n++) {
increase_reference(list -> list[n] =
(WOOL_OBJECT) WLString_make(p));
while(*p++);
}
XFree(prop);
return (WOOL_OBJECT) list;
}
return NIL;
}
/* When not-yet mapped configures its window, we must execute the
* configure request!
*/
ConfigureUnmappedWindow(evt)
XConfigureRequestEvent *evt;
{
XWindowChanges window_changes;
window_changes.x = evt -> x;
window_changes.y = evt -> y;
window_changes.width = evt -> width;
window_changes.height = evt -> height;
window_changes.border_width = evt -> border_width;
window_changes.sibling = evt -> above;
window_changes.stack_mode = evt -> detail;
TrapXErrors(XConfigureWindow(dpy,
evt -> window, evt -> value_mask,
&window_changes));
}
/* PROTOCOLS communication via user events
*/
send_protocol_message(window, protocol, timestamp, data_size, data)
Window window;
Atom protocol;
Time timestamp; /* 0 for CurrentTime */
int data_size; /* 0 to 3 32-bit quantities */
Card32 *data;
{
XClientMessageEvent event;
event.type = ClientMessage;
event.window = window;
event.message_type = XA_WM_PROTOCOLS;
event.format = 32;
event.data.l[0] = (long) protocol;
if (timestamp)
event.data.l[1] = (long) timestamp;
else
event.data.l[1] = (long) CurrentTime;
data_size = Min(3, data_size);
if (data_size)
bcopy(data, &(event.data.l[2]), data_size * 4);
if (data_size < 3)
bzero(&(event.data.l[2 + data_size]), (3 - data_size) * 4);
TrapXErrors(XSendEvent(dpy, window, False, 0, (XEvent *) &event));
}
/* WM_DELETE_WINDOW
* if window participate in protocol, sends message and returns TRUE
* else unmaps it and its associated icon (go to withdrawn) and returns NIL
*/
WOOL_OBJECT
wool_delete_window(argc, argv)
int argc;
WOOL_Number *argv;
{
ClientWindow cw = TargetWindow -> window;
if (argc) {
must_be_number(argv[0], 0);
cw = ((ClientWindow) argv[0] -> number) -> window;
}
if (cw -> client
&& cw -> cached_props -> wm_delete_window) {
send_protocol_message(cw -> client, XA_WM_DELETE_WINDOW,
GWMTime, 0, 0);
return TRU;
} else {
return NIL;
}
}
/* WM_SAVE_YOURSELF
* if window participate in protocol, sends message and returns TRUE
* else returns NIL
*/
WOOL_OBJECT
wool_save_yourself(argc, argv)
int argc;
WOOL_Number *argv;
{
ClientWindow cw = TargetWindow -> window;
if (argc) {
must_be_number(argv[0], 0);
cw = ((ClientWindow) argv[0] -> number) -> window;
}
if (cw -> client && !cw -> client_wob
&& cw -> cached_props -> wm_save_yourself) {
send_protocol_message(cw -> client, XA_WM_SAVE_YOURSELF,
GWMTime, 0, 0);
return TRU;
} else {
return NIL;
}
}
/* Gravity: 123
* 456
* 789
* (Gravity - 1) % 3: x: 0 1 2
* (Gravity - 1) / 3: y: 0 1 2
* 1 is center, -1 is just client mustn't move
*/
GetWinGravityHint(cw, xgrav, ygrav)
ClientWindow cw;
int *xgrav, *ygrav;
{
#ifdef NOBASEDIMS
*xgrav = *ygrav = -1;
#else /* NOBASEDIMS */
int grav = cw -> cached_props -> normal_hints.win_gravity;
if (cw -> cached_props -> normal_hints.flags & PWinGravity) {
if ((grav <= ForgetGravity) || (grav >= StaticGravity))
*xgrav = *ygrav = -1;
else {
*xgrav = (grav - 1) % 3;
*ygrav = (grav - 1) / 3;
}
} else {
*xgrav = *ygrav = 0; /* NorthWest is default */
}
#endif /* NOBASEDIMS */
}
/* obeys the Window Gravity hints
* direction is 1 on decoration, 0 on un-decoration
* adjusts in place the cw->box.x/y dims
*/
ObeyWinGravityHint(cw, direction)
ClientWindow cw;
int direction;
{
int x_gravity, y_gravity;
int x_offset, y_offset;
#ifdef NOBASEDIMS /* old pre -R4 code */
if (!GWM_ProcessingExistingWindows
&& !cw -> cached_props -> new_normal_hints) {
if (cw -> cached_props -> normal_hints.flags & USPosition) {
cw -> box.x = cw -> cached_props -> normal_hints.x;
cw -> box.y = cw -> cached_props -> normal_hints.y;
} else if (cw -> cached_props -> normal_hints.flags & PPosition) {
cw -> box.x = cw -> cached_props -> normal_hints.x;
cw -> box.y = cw -> cached_props -> normal_hints.y;
}
}
#endif /* NOBASEDIMS */
GetWinGravityHint(cw, &x_gravity, &y_gravity);
switch(x_gravity) {
case 0: /* west */
x_offset = 0;
break;
/* I dont beleive in this. I think 1 means that it shouldn't move in */
/* this direction, like the default below. / aho@nada.kth.se 950302 */
/* case 1: */ /* center */
/* x_offset = cw -> old_inner_borderwidth - cw -> box.borderwidth */
/* + (cw -> inner_width - cw -> box.width)/2; */
/* break; */
case 2: /* east */
x_offset = cw -> inner_width - cw -> box.width
+ 2 * (cw -> old_inner_borderwidth - cw -> box.borderwidth);
break;
default: /* client don't move */
x_offset = cw -> old_inner_borderwidth - cw -> inner_borderwidth
- cw -> inner_x - cw -> box.borderwidth;
}
switch(y_gravity) {
case 0: /* north */
y_offset = 0;
break;
/* I dont beleive in this. I think 1 means that it shouldn't move in */
/* this direction, like the default below. / aho@nada.kth.se 950302 */
/* case 1: */ /* center */
/* y_offset = cw -> old_inner_borderwidth - cw -> box.borderwidth */
/* + (cw -> inner_height - cw -> box.height)/2; */
/* break; */
case 2: /* south */
y_offset = cw -> inner_height - cw -> box.height
+ 2 * (cw -> old_inner_borderwidth - cw -> box.borderwidth);
break;
default: /* client don't move */
y_offset = cw -> old_inner_borderwidth - cw -> inner_borderwidth
- cw -> inner_y - cw -> box.borderwidth;
}
trace1(6, "window 0x%x:" , cw->client);
trace2(6, "at %d, %d, offseted by:" , cw->box.x, cw->box.y);
trace2(6, " %d, %d\n" , x_offset, y_offset);
cw->box.x += (direction ? x_offset : - x_offset);
cw->box.y += (direction ? y_offset : - y_offset);
}
/* misc utilities */
/* replaces ., blanks, and * by _ for using as class in Xrm
* returns arg if not changed, new string if changed so as not to modify
* the original string
*/
WOOL_OBJECT
MakeResourceIdentifier(string)
WOOL_String string;
{
char *p;
WOOL_String new_string = 0;
must_be_string(string, 1);
for (p = string->string; *p; p++)
if (*p == '.' || *p == '*' || *p == ' ' || *p == '\t') {
if (!new_string)
new_string = WLString_make(string->string);
new_string->string[p - string->string] = '_';
}
if (new_string)
return (WOOL_OBJECT) new_string;
else
return (WOOL_OBJECT) string;
}
/* create an unmapped window whose ID is stored on the root window of each
* managed screen in the GWM_RUNNING property
* current screen is given in the context
*/
CreateGwmLabelWindow()
{
XSetWindowAttributes wa;
wa.override_redirect = True;
Context->GwmWindow = XCreateWindow (dpy, Context->root,
-100, -100, 10, 10, 0, 0,
InputOnly, CopyFromParent,
CWOverrideRedirect,
&wa);
XChangeProperty(dpy, Context->root, XA_GWM_RUNNING, XA_GWM_RUNNING,
32, PropModeReplace, (unsigned char *) &(Context->GwmWindow), 1);
XChangeProperty(dpy, Context->GwmWindow, XA_GWM_RUNNING, XA_GWM_RUNNING,
32, PropModeReplace, (unsigned char *) &(Context->GwmWindow), 1);
}
/* FindToplevelWindow
* given a window, move back in hierarchy to return the toplevel one
* If given root, returns 0
*/
Window
FindToplevelWindow(w)
Window w;
{
Window parent = 0;
Window *children; /* list of root sons */
unsigned int nchildren; /* number of children */
Window root;
if (w == Context->root) {
return 0;
}
for (;;) {
if(!XQueryTree(dpy, w, &root, &parent, &children, &nchildren)) {
return 0; /* failed */
}
XFreeN(children);
if (parent == Context->root) {
return w;
}
w = parent;
}
}
/* IsAToplevelWindow
* Examine a non-gwm decorated window to see if it is legal to decorate it
*/
int
IsAToplevelWindow(w)
Window w;
{
Window dummywin, parent; /* dummy parent */
Window *children; /* list of root sons */
unsigned int nchildren; /* number of children */
if (XQueryTree(dpy, w, &dummywin, &parent, &children, &nchildren)) {
XFreeN(children);
if (parent == Context -> root) { /* w is son of root, OK */
return 1;
}
} /* then not valid! */
return 0;
}
/* ClientWindowAncestorOfWindow
* looks into all the ancestors to eventually find a ClientWindow, which would
* be returned, or 0
*/
ClientWindow
ClientWindowAncestorOfWindow(w)
Window w;
{
Window toplevel = FindToplevelWindow(w);
ClientWindow cw;
if (toplevel
&& (cw = (ClientWindow) LookUpWob(toplevel))
&& (cw -> status & ClientWindowStatus)) {
return cw;
} else {
return 0;
}
}
/* DecoratedWindow
* given a X window, returns the GWM window associated to, decorating it in
* the process if needed.
* used to look for window group leaders and transient_for masters
* returns 0 if failed
*/
ClientWindow
DecoratedWindow(w)
Window w;
{
ClientWindow tw;
ClientWindow cw = (ClientWindow) LookUpClient(w);
if (cw) {
return cw;
} else {
cw = (ClientWindow) LookUpWob(w);
if (cw) {
if (cw->status | ClientWindowStatus) {
return 0;
} else {
Window win = FindToplevelWindow(w);
if (win) {
ClientWindow old_windec = GWM_window_being_decorated;
int old_reenter = GWM_reenter_on_opens;
GWM_reenter_on_opens = 0;
cw = (ClientWindow) DecorateWindow(win, Context->rootWob, 1, 0);
GWM_reenter_on_opens = old_reenter;
GWM_window_being_decorated = old_windec;
return cw;
} else {
return 0;
}
}
} else if (tw = ClientWindowAncestorOfWindow(w)) {
return tw;
} else if (IsAToplevelWindow(w)) {
ClientWindow old_windec = GWM_window_being_decorated;
int old_reenter = GWM_reenter_on_opens;
GWM_reenter_on_opens = 0;
cw = (ClientWindow) DecorateWindow(w, Context->rootWob, 1, 0);
GWM_reenter_on_opens = old_reenter;
GWM_window_being_decorated = old_windec;
return cw;
}
}
return 0;
}