593 lines
18 KiB
C
593 lines
18 KiB
C
#include "openbox.h"
|
|
#include "client.h"
|
|
#include "xerror.h"
|
|
#include "prop.h"
|
|
#include "screen.h"
|
|
#include "frame.h"
|
|
#include "focus.h"
|
|
#include "hooks.h"
|
|
#include "stacking.h"
|
|
#include "kbind.h"
|
|
#include "mbind.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/keysym.h>
|
|
#include <X11/Xatom.h>
|
|
|
|
static void event_process(XEvent *e);
|
|
static void event_handle_root(XEvent *e);
|
|
static void event_handle_client(Client *c, XEvent *e);
|
|
|
|
Time event_lasttime = 0;
|
|
|
|
/*! A list of all possible combinations of keyboard lock masks */
|
|
static unsigned int mask_list[8];
|
|
/*! The value of the mask for the NumLock modifier */
|
|
static unsigned int NumLockMask;
|
|
/*! The value of the mask for the ScrollLock modifier */
|
|
static unsigned int ScrollLockMask;
|
|
/*! The key codes for the modifier keys */
|
|
static XModifierKeymap *modmap;
|
|
/*! Table of the constant modifier masks */
|
|
static const int mask_table[] = {
|
|
ShiftMask, LockMask, ControlMask, Mod1Mask,
|
|
Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
|
|
};
|
|
static int mask_table_size;
|
|
|
|
void event_startup()
|
|
{
|
|
mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
|
|
|
|
/* get lock masks that are defined by the display (not constant) */
|
|
modmap = XGetModifierMapping(ob_display);
|
|
g_assert(modmap);
|
|
if (modmap && modmap->max_keypermod > 0) {
|
|
size_t cnt;
|
|
const size_t size = mask_table_size * modmap->max_keypermod;
|
|
/* get the values of the keyboard lock modifiers
|
|
Note: Caps lock is not retrieved the same way as Scroll and Num
|
|
lock since it doesn't need to be. */
|
|
const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
|
|
const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
|
|
XK_Scroll_Lock);
|
|
|
|
for (cnt = 0; cnt < size; ++cnt) {
|
|
if (! modmap->modifiermap[cnt]) continue;
|
|
|
|
if (num_lock == modmap->modifiermap[cnt])
|
|
NumLockMask = mask_table[cnt / modmap->max_keypermod];
|
|
if (scroll_lock == modmap->modifiermap[cnt])
|
|
ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
|
|
}
|
|
}
|
|
|
|
mask_list[0] = 0;
|
|
mask_list[1] = LockMask;
|
|
mask_list[2] = NumLockMask;
|
|
mask_list[3] = LockMask | NumLockMask;
|
|
mask_list[4] = ScrollLockMask;
|
|
mask_list[5] = ScrollLockMask | LockMask;
|
|
mask_list[6] = ScrollLockMask | NumLockMask;
|
|
mask_list[7] = ScrollLockMask | LockMask | NumLockMask;
|
|
}
|
|
|
|
void event_shutdown()
|
|
{
|
|
XFreeModifiermap(modmap);
|
|
}
|
|
|
|
void event_loop()
|
|
{
|
|
fd_set selset;
|
|
XEvent e;
|
|
int x_fd;
|
|
|
|
while (TRUE) {
|
|
/*
|
|
There are slightly different event retrieval semantics here for
|
|
local (or high bandwidth) versus remote (or low bandwidth)
|
|
connections to the display/Xserver.
|
|
*/
|
|
if (ob_remote) {
|
|
if (!XPending(ob_display))
|
|
break;
|
|
} else {
|
|
/*
|
|
This XSync allows for far more compression of events, which
|
|
makes things like Motion events perform far far better. Since
|
|
it also means network traffic for every event instead of every
|
|
X events (where X is the number retrieved at a time), it
|
|
probably should not be used for setups where Openbox is
|
|
running on a remote/low bandwidth display/Xserver.
|
|
*/
|
|
XSync(ob_display, FALSE);
|
|
if (!XEventsQueued(ob_display, QueuedAlready))
|
|
break;
|
|
}
|
|
XNextEvent(ob_display, &e);
|
|
|
|
event_process(&e);
|
|
}
|
|
|
|
x_fd = ConnectionNumber(ob_display);
|
|
FD_ZERO(&selset);
|
|
FD_SET(x_fd, &selset);
|
|
select(x_fd + 1, &selset, NULL, NULL, NULL);
|
|
}
|
|
|
|
void event_process(XEvent *e)
|
|
{
|
|
XEvent ce;
|
|
KeyCode *kp;
|
|
Window window;
|
|
int i, k;
|
|
Client *client;
|
|
GQuark context;
|
|
static guint motion_button = 0;
|
|
|
|
/* pick a window */
|
|
switch (e->type) {
|
|
case UnmapNotify:
|
|
window = e->xunmap.window;
|
|
break;
|
|
case DestroyNotify:
|
|
window = e->xdestroywindow.window;
|
|
break;
|
|
case ConfigureRequest:
|
|
window = e->xconfigurerequest.window;
|
|
break;
|
|
default:
|
|
window = e->xany.window;
|
|
}
|
|
|
|
/* grab the lasttime and hack up the state */
|
|
switch (e->type) {
|
|
case ButtonPress:
|
|
case ButtonRelease:
|
|
event_lasttime = e->xbutton.time;
|
|
e->xbutton.state &= ~(LockMask | NumLockMask | ScrollLockMask);
|
|
/* kill off the Button1Mask etc, only want the modifiers */
|
|
e->xbutton.state &= (ControlMask | ShiftMask | Mod1Mask |
|
|
Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
|
|
break;
|
|
case KeyPress:
|
|
event_lasttime = e->xkey.time;
|
|
e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
|
|
/* kill off the Button1Mask etc, only want the modifiers */
|
|
e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
|
|
Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
|
|
/* add to the state the mask of the modifier being pressed, if it is
|
|
a modifier key being pressed (this is a little ugly..) */
|
|
/* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
|
|
/* kp = modmap->modifiermap;*/
|
|
/* for (i = 0; i < mask_table_size; ++i) {*/
|
|
/* for (k = 0; k < modmap->max_keypermod; ++k) {*/
|
|
/* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
|
|
/* add the mask for it */
|
|
/* e->xkey.state |= mask_table[i];*/
|
|
/* cause the first loop to break; */
|
|
/* i = mask_table_size;*/
|
|
/* break;*/ /* get outta here! */
|
|
/* }*/
|
|
/* ++kp;*/
|
|
/* }*/
|
|
/* }*/
|
|
|
|
break;
|
|
case KeyRelease:
|
|
event_lasttime = e->xkey.time;
|
|
e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
|
|
/* kill off the Button1Mask etc, only want the modifiers */
|
|
e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
|
|
Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
|
|
/* remove from the state the mask of the modifier being released, if
|
|
it is a modifier key being released (this is a little ugly..) */
|
|
kp = modmap->modifiermap;
|
|
for (i = 0; i < mask_table_size; ++i) {
|
|
for (k = 0; k < modmap->max_keypermod; ++k) {
|
|
if (*kp == e->xkey.keycode) { /* found the keycode */
|
|
/* remove the mask for it */
|
|
e->xkey.state &= ~mask_table[i];
|
|
/* cause the first loop to break; */
|
|
i = mask_table_size;
|
|
break; /* get outta here! */
|
|
}
|
|
++kp;
|
|
}
|
|
}
|
|
break;
|
|
case MotionNotify:
|
|
event_lasttime = e->xmotion.time;
|
|
e->xmotion.state &= ~(LockMask | NumLockMask | ScrollLockMask);
|
|
/* kill off the Button1Mask etc, only want the modifiers */
|
|
e->xmotion.state &= (ControlMask | ShiftMask | Mod1Mask |
|
|
Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
|
|
/* compress events */
|
|
while (XCheckTypedWindowEvent(ob_display, window, e->type, &ce)) {
|
|
e->xmotion.x_root = ce.xmotion.x_root;
|
|
e->xmotion.y_root = ce.xmotion.y_root;
|
|
}
|
|
break;
|
|
case PropertyNotify:
|
|
event_lasttime = e->xproperty.time;
|
|
break;
|
|
case FocusIn:
|
|
case FocusOut:
|
|
if (e->xfocus.mode == NotifyGrab)
|
|
/*|| e.xfocus.mode == NotifyUngrab ||*/
|
|
|
|
/* From Metacity, from WindowMaker, ignore all funky pointer
|
|
root events. Its commented out cuz I don't think we need this
|
|
at all. If problems arise we can look into it */
|
|
/*e.xfocus.detail > NotifyNonlinearVirtual) */
|
|
return; /* skip me! */
|
|
if (e->type == FocusOut) {
|
|
/* FocusOut events just make us look for FocusIn events. They
|
|
are mostly ignored otherwise. */
|
|
XEvent fi;
|
|
if (XCheckTypedEvent(ob_display, FocusIn, &fi)) {
|
|
event_process(&fi);
|
|
/* dont unfocus the window we just focused! */
|
|
if (fi.xfocus.window == e->xfocus.window)
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case EnterNotify:
|
|
case LeaveNotify:
|
|
event_lasttime = e->xcrossing.time;
|
|
if (e->xcrossing.mode != NotifyNormal)
|
|
return; /* skip me! */
|
|
break;
|
|
}
|
|
|
|
client = g_hash_table_lookup(client_map, (gpointer)window);
|
|
if (client) {
|
|
event_handle_client(client, e);
|
|
} else if (window == ob_root)
|
|
event_handle_root(e);
|
|
else if (e->type == ConfigureRequest) {
|
|
/* unhandled configure requests must be used to configure the
|
|
window directly */
|
|
XWindowChanges xwc;
|
|
|
|
xwc.x = e->xconfigurerequest.x;
|
|
xwc.y = e->xconfigurerequest.y;
|
|
xwc.width = e->xconfigurerequest.width;
|
|
xwc.height = e->xconfigurerequest.height;
|
|
xwc.border_width = e->xconfigurerequest.border_width;
|
|
xwc.sibling = e->xconfigurerequest.above;
|
|
xwc.stack_mode = e->xconfigurerequest.detail;
|
|
|
|
g_message("Proxying configure event for 0x%lx\n", window);
|
|
|
|
/* we are not to be held responsible if someone sends us an
|
|
invalid request! */
|
|
xerror_set_ignore(TRUE);
|
|
XConfigureWindow(ob_display, window,
|
|
e->xconfigurerequest.value_mask, &xwc);
|
|
xerror_set_ignore(FALSE);
|
|
}
|
|
|
|
/* dispatch Crossing, Pointer and Key events to the hooks */
|
|
switch(e->type) {
|
|
case EnterNotify:
|
|
context = frame_get_context(client, window);
|
|
LOGICALHOOK(EnterWindow, context, client);
|
|
break;
|
|
case LeaveNotify:
|
|
context = frame_get_context(client, window);
|
|
LOGICALHOOK(LeaveWindow, context, client);
|
|
break;
|
|
case ButtonPress:
|
|
if (!motion_button) motion_button = e->xbutton.button;
|
|
context = frame_get_context(client, window);
|
|
mbind_fire(e->xbutton.state, e->xbutton.button, context,
|
|
Pointer_Press, client, e->xbutton.x_root,
|
|
e->xbutton.y_root);
|
|
break;
|
|
case ButtonRelease:
|
|
if (motion_button == e->xbutton.button) motion_button = 0;
|
|
context = frame_get_context(client, window);
|
|
mbind_fire(e->xbutton.state, e->xbutton.button, context,
|
|
Pointer_Release, client, e->xbutton.x_root,
|
|
e->xbutton.y_root);
|
|
break;
|
|
case MotionNotify:
|
|
context = frame_get_context(client, window);
|
|
mbind_fire(e->xkey.state, motion_button, context, Pointer_Motion,
|
|
client, e->xmotion.x_root, e->xmotion.y_root);
|
|
break;
|
|
case KeyPress:
|
|
kbind_fire(e->xkey.state, e->xkey.keycode, TRUE);
|
|
break;
|
|
case KeyRelease:
|
|
kbind_fire(e->xkey.state, e->xkey.keycode, FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void event_handle_root(XEvent *e)
|
|
{
|
|
Atom msgtype;
|
|
|
|
switch(e->type) {
|
|
case MapRequest:
|
|
g_message("MapRequest on root");
|
|
client_manage(e->xmap.window);
|
|
break;
|
|
case ClientMessage:
|
|
if (e->xclient.format != 32) break;
|
|
|
|
msgtype = e->xclient.message_type;
|
|
if (msgtype == prop_atoms.net_current_desktop) {
|
|
unsigned int d = e->xclient.data.l[0];
|
|
if (d <= screen_num_desktops)
|
|
screen_set_desktop(d);
|
|
} else if (msgtype == prop_atoms.net_number_of_desktops) {
|
|
unsigned int d = e->xclient.data.l[0];
|
|
if (d > 0)
|
|
screen_set_num_desktops(d);
|
|
} else if (msgtype == prop_atoms.net_showing_desktop) {
|
|
screen_show_desktop(e->xclient.data.l[0] != 0);
|
|
}
|
|
break;
|
|
case PropertyNotify:
|
|
if (e->xproperty.atom == prop_atoms.net_desktop_names)
|
|
screen_update_desktop_names();
|
|
else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
|
|
screen_update_layout();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void event_handle_client(Client *client, XEvent *e)
|
|
{
|
|
XEvent ce;
|
|
Atom msgtype;
|
|
|
|
switch (e->type) {
|
|
case FocusIn:
|
|
client->focused = TRUE;
|
|
frame_adjust_focus(client->frame);
|
|
|
|
/* focus state can affect the stacking layer */
|
|
client_calc_layer(client);
|
|
|
|
focus_set_client(client);
|
|
break;
|
|
case FocusOut:
|
|
client->focused = FALSE;
|
|
frame_adjust_focus(client->frame);
|
|
|
|
/* focus state can affect the stacking layer */
|
|
client_calc_layer(client);
|
|
|
|
if (focus_client == client)
|
|
focus_set_client(NULL);
|
|
break;
|
|
case ConfigureRequest:
|
|
g_message("ConfigureRequest for window %lx", client->window);
|
|
/* compress these */
|
|
while (XCheckTypedWindowEvent(ob_display, client->window,
|
|
ConfigureRequest, &ce)) {
|
|
/* XXX if this causes bad things.. we can compress config req's
|
|
with the same mask. */
|
|
e->xconfigurerequest.value_mask |=
|
|
ce.xconfigurerequest.value_mask;
|
|
if (ce.xconfigurerequest.value_mask & CWX)
|
|
e->xconfigurerequest.x = ce.xconfigurerequest.x;
|
|
if (ce.xconfigurerequest.value_mask & CWY)
|
|
e->xconfigurerequest.y = ce.xconfigurerequest.y;
|
|
if (ce.xconfigurerequest.value_mask & CWWidth)
|
|
e->xconfigurerequest.width = ce.xconfigurerequest.width;
|
|
if (ce.xconfigurerequest.value_mask & CWHeight)
|
|
e->xconfigurerequest.height = ce.xconfigurerequest.height;
|
|
if (ce.xconfigurerequest.value_mask & CWBorderWidth)
|
|
e->xconfigurerequest.border_width =
|
|
ce.xconfigurerequest.border_width;
|
|
if (ce.xconfigurerequest.value_mask & CWStackMode)
|
|
e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
|
|
}
|
|
|
|
/* if we are iconic (or shaded (fvwm does this)) ignore the event */
|
|
if (client->iconic || client->shaded) return;
|
|
|
|
if (e->xconfigurerequest.value_mask & CWBorderWidth)
|
|
client->border_width = e->xconfigurerequest.border_width;
|
|
|
|
/* resize, then move, as specified in the EWMH section 7.7 */
|
|
if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
|
|
CWX | CWY)) {
|
|
int x, y, w, h;
|
|
Corner corner;
|
|
|
|
x = (e->xconfigurerequest.value_mask & CWX) ?
|
|
e->xconfigurerequest.x : client->area.x;
|
|
y = (e->xconfigurerequest.value_mask & CWY) ?
|
|
e->xconfigurerequest.y : client->area.y;
|
|
w = (e->xconfigurerequest.value_mask & CWWidth) ?
|
|
e->xconfigurerequest.width : client->area.width;
|
|
h = (e->xconfigurerequest.value_mask & CWHeight) ?
|
|
e->xconfigurerequest.height : client->area.height;
|
|
|
|
switch (client->gravity) {
|
|
case NorthEastGravity:
|
|
case EastGravity:
|
|
corner = Corner_TopRight;
|
|
break;
|
|
case SouthWestGravity:
|
|
case SouthGravity:
|
|
corner = Corner_BottomLeft;
|
|
break;
|
|
case SouthEastGravity:
|
|
corner = Corner_BottomRight;
|
|
break;
|
|
default: /* NorthWest, Static, etc */
|
|
corner = Corner_TopLeft;
|
|
}
|
|
|
|
client_configure(client, corner, x, y, w, h, FALSE, FALSE);
|
|
}
|
|
|
|
if (e->xconfigurerequest.value_mask & CWStackMode) {
|
|
switch (e->xconfigurerequest.detail) {
|
|
case Below:
|
|
case BottomIf:
|
|
stacking_lower(client);
|
|
break;
|
|
|
|
case Above:
|
|
case TopIf:
|
|
default:
|
|
stacking_raise(client);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case UnmapNotify:
|
|
if (client->ignore_unmaps) {
|
|
client->ignore_unmaps--;
|
|
break;
|
|
}
|
|
g_message("UnmapNotify for %lx", client->window);
|
|
client_unmanage(client);
|
|
break;
|
|
case DestroyNotify:
|
|
g_message("DestroyNotify for %lx", client->window);
|
|
client_unmanage(client);
|
|
break;
|
|
case ReparentNotify:
|
|
/* this is when the client is first taken captive in the frame */
|
|
if (e->xreparent.parent == client->frame->plate) break;
|
|
|
|
/*
|
|
This event is quite rare and is usually handled in unmapHandler.
|
|
However, if the window is unmapped when the reparent event occurs,
|
|
the window manager never sees it because an unmap event is not sent
|
|
to an already unmapped window.
|
|
*/
|
|
|
|
/* we don't want the reparent event, put it back on the stack for the
|
|
X server to deal with after we unmanage the window */
|
|
XPutBackEvent(ob_display, e);
|
|
|
|
client_unmanage(client);
|
|
break;
|
|
case MapRequest:
|
|
/* we shouldn't be able to get this unless we're iconic */
|
|
g_assert(client->iconic);
|
|
|
|
LOGICALHOOK(RequestActivate, g_quark_try_string("client"), client);
|
|
break;
|
|
case ClientMessage:
|
|
/* validate cuz we query stuff off the client here */
|
|
if (!client_validate(client)) break;
|
|
|
|
if (e->xclient.format != 32) return;
|
|
|
|
msgtype = e->xclient.message_type;
|
|
if (msgtype == prop_atoms.wm_change_state) {
|
|
/* compress changes into a single change */
|
|
while (XCheckTypedWindowEvent(ob_display, e->type,
|
|
client->window, &ce)) {
|
|
/* XXX: it would be nice to compress ALL messages of a
|
|
type, not just messages in a row without other
|
|
message types between. */
|
|
if (ce.xclient.message_type != msgtype) {
|
|
XPutBackEvent(ob_display, &ce);
|
|
break;
|
|
}
|
|
e->xclient = ce.xclient;
|
|
}
|
|
client_set_wm_state(client, e->xclient.data.l[0]);
|
|
} else if (msgtype == prop_atoms.net_wm_desktop) {
|
|
/* compress changes into a single change */
|
|
while (XCheckTypedWindowEvent(ob_display, e->type,
|
|
client->window, &ce)) {
|
|
/* XXX: it would be nice to compress ALL messages of a
|
|
type, not just messages in a row without other
|
|
message types between. */
|
|
if (ce.xclient.message_type != msgtype) {
|
|
XPutBackEvent(ob_display, &ce);
|
|
break;
|
|
}
|
|
e->xclient = ce.xclient;
|
|
}
|
|
client_set_desktop(client, e->xclient.data.l[0]);
|
|
} else if (msgtype == prop_atoms.net_wm_state) {
|
|
/* can't compress these */
|
|
g_message("net_wm_state %s %ld %ld for 0x%lx\n",
|
|
(e->xclient.data.l[0] == 0 ? "Remove" :
|
|
e->xclient.data.l[0] == 1 ? "Add" :
|
|
e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
|
|
e->xclient.data.l[1], e->xclient.data.l[2],
|
|
client->window);
|
|
client_set_state(client, e->xclient.data.l[0],
|
|
e->xclient.data.l[1], e->xclient.data.l[2]);
|
|
} else if (msgtype == prop_atoms.net_close_window) {
|
|
g_message("net_close_window for 0x%lx\n", client->window);
|
|
client_close(client);
|
|
} else if (msgtype == prop_atoms.net_active_window) {
|
|
g_message("net_active_window for 0x%lx\n", client->window);
|
|
if (screen_showing_desktop)
|
|
screen_show_desktop(FALSE);
|
|
if (client->iconic)
|
|
client_iconify(client, FALSE, TRUE);
|
|
else if (!client->frame->visible)
|
|
/* if its not visible for other reasons, then don't mess
|
|
with it */
|
|
return;
|
|
LOGICALHOOK(RequestActivate, g_quark_try_string("client"), client);
|
|
}
|
|
break;
|
|
case PropertyNotify:
|
|
/* validate cuz we query stuff off the client here */
|
|
if (!client_validate(client)) break;
|
|
|
|
/* compress changes to a single property into a single change */
|
|
while (XCheckTypedWindowEvent(ob_display, e->type,
|
|
client->window, &ce)) {
|
|
/* XXX: it would be nice to compress ALL changes to a property,
|
|
not just changes in a row without other props between. */
|
|
if (ce.xproperty.atom != e->xproperty.atom) {
|
|
XPutBackEvent(ob_display, &ce);
|
|
break;
|
|
}
|
|
}
|
|
|
|
msgtype = e->xproperty.atom;
|
|
if (msgtype == XA_WM_NORMAL_HINTS) {
|
|
client_update_normal_hints(client);
|
|
/* normal hints can make a window non-resizable */
|
|
client_setup_decor_and_functions(client);
|
|
} else if (msgtype == XA_WM_HINTS)
|
|
client_update_wmhints(client);
|
|
else if (msgtype == XA_WM_TRANSIENT_FOR) {
|
|
client_update_transient_for(client);
|
|
client_get_type(client);
|
|
/* type may have changed, so update the layer */
|
|
client_calc_layer(client);
|
|
client_setup_decor_and_functions(client);
|
|
}
|
|
else if (msgtype == prop_atoms.net_wm_name ||
|
|
msgtype == prop_atoms.wm_name)
|
|
client_update_title(client);
|
|
else if (msgtype == prop_atoms.net_wm_icon_name ||
|
|
msgtype == prop_atoms.wm_icon_name)
|
|
client_update_icon_title(client);
|
|
else if (msgtype == prop_atoms.wm_class)
|
|
client_update_class(client);
|
|
else if (msgtype == prop_atoms.wm_protocols) {
|
|
client_update_protocols(client);
|
|
client_setup_decor_and_functions(client);
|
|
}
|
|
else if (msgtype == prop_atoms.net_wm_strut)
|
|
client_update_strut(client);
|
|
else if (msgtype == prop_atoms.net_wm_icon)
|
|
client_update_icons(client);
|
|
else if (msgtype == prop_atoms.kwm_win_icon)
|
|
client_update_kwm_icon(client);
|
|
}
|
|
}
|