/* 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 #include "wool.h" #include "wl_atom.h" #include "wl_list.h" #include "wl_string.h" #include "gwm.h" #include #include #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; }