/*-
* Copyright (c) 2001 Jordan DeLong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "wm.h"
/* our handler table */
typedef void (*event_handler_t)(XEvent *event);
static event_handler_t handlers[LASTEvent];
/* handler for DestroyNotify events */
static void event_destroy_notify(XEvent *event) {
XDestroyWindowEvent *e = &event->xdestroywindow;
client_t *client;
if (XFindContext(display, e->window, client_context, (XPointer *) &client))
return;
if (e->window != client->window)
return;
if (!client->flags.internal)
XRemoveFromSaveSet(display, client->window);
plugin_window_death(client);
client_rm(client);
}
/* handler for ConfigureRequest */
static void event_configure_request(XEvent *event) {
XConfigureRequestEvent *e = &event->xconfigurerequest;
XWindowChanges wc;
client_t *client;
/* now we only update the structure if the window has one */
if (XFindContext(display, e->window, client_context, (XPointer *) &client))
client = NULL;
/*
* we need to respond to ConfigureRequests of windows that
* haven't yet been mapped, and thus don't have client_t structs
* in our list.
*/
if (!client) {
wc.x = e->x;
wc.y = e->y;
wc.width = e->width;
wc.height = e->height;
wc.border_width = e->border_width;
wc.stack_mode = e->detail;
XConfigureWindow(display, e->window, e->value_mask, &wc);
return;
}
/* the event should be for the client's window */
assert(e->window == client->window);
/*
* now for managed windows we do stuff to our managed
* client structure.
*/
if (e->value_mask & CWX)
client->x = e->x;
if (e->value_mask & CWY)
client->y = e->y;
if (e->value_mask & CWWidth)
client->width = e->width;
if (e->value_mask & CWHeight)
client->height = e->height;
/*
* when a client moves itself, it may need
* to change workspaces
*/
client_gravitate(client);
client_sizeframe(client);
action_send_config(client);
workspace_add_bypos(client->screen->desktop, client);
plugin_geometry_change(client);
}
/* handler for MapRequest events */
static void event_map_request(XEvent *event) {
XMapRequestEvent *e = &event->xmaprequest;
screen_t *screen;
client_t *client;
/*
* for some reason some broken clients (netscape) seem to do more
* than one map request on top window..., so make sure this window
* isn't already managed.
*/
if (!XFindContext(display, e->window, client_context, (XPointer *) &client))
return;
if (XFindContext(display, e->parent, root_context, (XPointer *) &screen))
return;
/*
* give plugins a chance to handle this window exclusively,
* if plugin_map_request returns PLUGIN_USING we don't managed
* in with client_add.
*/
if (plugin_map_request(screen, e))
return;
client = client_add(screen, e->window, NULL, NULL);
if (!client)
return;
client_map(client, 1);
}
/* handler for MapNotify events */
static void event_map_notify(XEvent *event) {
XMapEvent *e = &event->xmap;
client_t *client;
/*
* look for the client context we created for it in event_map_request;
* and only continue if this is a notify from the client->window; if
* it's for the frame we don't care here.
*/
if (XFindContext(display, e->window, client_context, (XPointer *) &client))
return;
if (e->window != client->window)
return;
/* give focus to new windows (if the options say to) */
if (options.focus_new && client->workspace)
focus_setfocused(client);
}
/* handler for UnmapNotify events */
static void event_unmap_notify(XEvent *event) {
XUnmapEvent *e = &event->xunmap;
client_t *client;
/*
* handle unmap notify events on managed windows; we only are
* interested in the unmapnotify events generated when the
* client window (not the frame) unmaps; because that signals
* that the client should be unmanaged. the only exception
* is when we get unmap events from a reparent (see below).
*/
if (XFindContext(display, e->window, client_context, (XPointer *) &client))
return;
if (e->window != client->window)
return;
/*
* see if the event came from reparenting
* (see screen.c, manage_existing_windows)
*/
if (client->flags.unmap_from_reparent) {
client_map(client, 0);
client->flags.unmap_from_reparent = 0;
return;
}
/*
* get rid of our client structure, reparent it to the root,
* and remove from our saveset
*/
if (client->state == NormalState)
XUnmapWindow(display, client->frame);
plugin_window_death(client);
client_setstate(client, WithdrawnState);
if (!client->flags.internal)
XRemoveFromSaveSet(display, client->window);
XReparentWindow(display, client->window, client->screen->root, client->x, client->y);
client_rm(client);
}
/* handler for ButtonPress events */
static void event_button_press(XEvent *event) {
XButtonEvent *e = &event->xbutton;
client_t *client;
screen_t *screen;
decor_t *decor;
/*
* if the click doesn't have a client context, check if it is
* a root window click; in which case notify plugins.
*/
if (XFindContext(display, e->window, client_context, (XPointer *) &client)) {
if (!XFindContext(display, e->window, root_context, (XPointer *) &screen))
plugin_root_button(screen, e);
return;
}
/*
* handle button presses on client frame window (for mouse_modifier
* grabs), the client window to do focusing in click focus mode,
* or client decoration.
*/
if (e->window == client->frame) {
if (options.focus == FOCUS_CLICK)
focus_setfocused(client);
/*
* either do move/resize for mouse_modifier grabs, or in
* click focus mode we will get events for initial clicks
* in the click->frame to do a raise.
*
* XXX: the buttons that do which for mouse_modifier should
* be configurable.
*/
if ((e->state & ~(numlock_mask | scroll_mask | LockMask)) == options.mouse_modifier) {
if (e->button == Button3)
action_resize(client);
else
action_move(client);
} else {
XAllowEvents(display, ReplayPointer, CurrentTime);
stacking_raise(client);
}
} else if (!XFindContext(display, e->window, decor_context, (XPointer *) &decor)) {
if (options.focus == FOCUS_CLICK)
focus_setfocused(client);
decor_handlepress(client, decor, e);
}
}
/* handler for ButtonRelease events */
static void event_button_release(XEvent *event) {
XButtonEvent *e = &event->xbutton;
screen_t *screen;
/*
* the only clicks we are concerned with are clicks on
* a managed root window.
*/
if (XFindContext(display, e->window, root_context, (XPointer *) &screen))
return;
plugin_root_button(screen, e);
/*
* switch workspace when clicking on the edges of the
* screen.
*
* XXX: this should likely be removed at some point...
*/
if (e->button == Button1) {
if (e->y_root == 0) {
if (workspace_viewport_move(screen, screen->desktop, 0, -1))
XWarpPointer(display, None, screen->root, 0, 0, 1, 1,
e->x_root, screen->height - 1);
} else if (e->y_root == screen->height - 1) {
if (workspace_viewport_move(screen, screen->desktop, 0, 1))
XWarpPointer(display, None, screen->root, 0, 0, 1, 1,
e->x_root, 0);
} else if (e->x_root == 0) {
if (workspace_viewport_move(screen, screen->desktop, -1, 0))
XWarpPointer(display, None, screen->root, 0, 0, 1, 1,
screen->width - 1, e->y_root);
} else if (e->x_root == screen->width - 1) {
if (workspace_viewport_move(screen, screen->desktop, 1, 0))
XWarpPointer(display, None, screen->root, 0, 0, 1, 1,
0, e->y_root);
}
}
}
/* handler for KeyPress events */
static void event_key_press(XEvent *event) {
XKeyEvent *e = &event->xkey;
screen_t *screen;
/*
* handle dispatching key bindings; get the screen on which
* the event occured and pass it to the key_press handler.
*/
if (XFindContext(display, e->root, root_context, (XPointer *) &screen))
return;
keys_press(screen, e);
}
/* handler for Expose events */
static void event_expose(XEvent *event) {
XExposeEvent *e = &event->xexpose;
client_t *client;
decor_t *decor;
/*
* handle exposures for client decoration.
*/
if (XFindContext(display, e->window, client_context, (XPointer *) &client))
return;
if (XFindContext(display, e->window, decor_context, (XPointer *) &decor))
return;
decor_expose(client, decor, e);
}
/* handler for ClientMessage */
static void event_client_message(XEvent *event) {
XClientMessageEvent *e = &event->xclient;
client_t *client;
if (XFindContext(display, e->window, client_context, (XPointer *) &client))
return;
if (e->window != client->window)
return;
/*
* currently, only handle state change requests
* for iconification.
*/
if (e->message_type == WM_CHANGE_STATE && e->format == 32
&& e->data.l[0] == IconicState
&& client->state != IconicState)
action_iconify(client);
}
/* handler for PropertyNotify */
static void event_property_notify(XEvent *event) {
XPropertyEvent *e = &event->xproperty;
client_t *client;
long supplied;
#ifdef I18N
XTextProperty text_prop;
char **cl;
int n;
#endif
/* get associated client, the event must be for it's window */
if (XFindContext(display, e->window, client_context, (XPointer *) &client))
return;
assert(e->window == client->window);
/*
* notify plugins about the property event; plugins can use this
* be aware of client title changes, or clients changing hinting
* properties. if a plugin is using the property change, and is
* certain noone else will be (i.e. a specific windowmanager hint
* protocol) it may return PLUGIN_USING, in which case we don't
* need to do anything else for this event.
*/
if (plugin_property_notify(client, e) == PLUGIN_USING)
return;
/*
* support changing the normal hints for a managed client
* window, and changing the client store name.
*/
if (e->state == PropertyNewValue)
switch (e->atom) {
case XA_WM_NAME:
if (client->store_name)
XFree(client->store_name);
#ifdef I18N
if (XGetWMName(display, client->window, &text_prop) != 0) {
if (text_prop.encoding == XA_STRING) {
client->store_name = (char *)text_prop.value;
} else {
XmbTextPropertyToTextList(display, &text_prop, &cl, &n);
if (cl) {
client->store_name = strdup(cl[0]);
XFreeStringList(cl);
} else {
client->store_name = "NoName";
}
}
} else {
client->store_name = "NoName";
}
#else
XFetchName(display, client->window, &client->store_name);
#endif
/*
* XXX: need a callback to let plugins know
*/
if (client->state != IconicState)
decor_titlechange(client);
break;
case XA_WM_NORMAL_HINTS:
XGetWMNormalHints(display, client->window, &client->normalhints,
&supplied);
break;
}
}
/* handler for EnterNotify */
static void event_enter_notify(XEvent *event) {
XCrossingEvent *e = &event->xcrossing;
client_t *client;
/*
* we should only get this event for sloppy/pointer focusing
* modes; when entering the frame window in those modes, we
* set the focus to that client.
*/
if (XFindContext(display, e->window, client_context, (XPointer *) &client))
return;
if (e->window != client->frame)
return;
focus_setfocused(client);
}
/* handler for LeaveNotify */
static void event_leave_notify(XEvent *event) {
XCrossingEvent *e = &event->xcrossing;
client_t *client;
/*
* we should only recieve this event for pointer focusing
* mode; when leaving a window in pointer focus, the window
* is unfocused.
*/
if (XFindContext(display, e->window, client_context, (XPointer *) &client))
return;
if (e->window != client->frame)
return;
/*
* we check if the client has a workspace first, because we will get
* a leave notify if the user iconifies a window, and the window
* will have left it's workspace, and thus already be unfocused.
*/
if (client->workspace)
focus_unfocus(client);
}
/* handler for ShapeNotify */
static void event_shape_notify(XEvent *event) {
client_t *client;
/*
* when a client shapes it's window, we need to shape the frame
* window accordingly.
*/
if (XFindContext(display, event->xany.window, client_context, (XPointer *) &client))
return;
if (event->xany.window != client->window)
return;
client_shape(client);
}
/*
* set up the table of event handlers; NULL out the events
* that we don't handle.
*/
static void event_init() {
int i;
for (i = 0; i < LASTEvent; i++)
handlers[i] = NULL;
handlers[DestroyNotify] = event_destroy_notify;
handlers[ConfigureRequest] = event_configure_request;
handlers[MapRequest] = event_map_request;
handlers[MapNotify] = event_map_notify;
handlers[UnmapNotify] = event_unmap_notify;
handlers[ButtonPress] = event_button_press;
handlers[ButtonRelease] = event_button_release;
handlers[KeyPress] = event_key_press;
handlers[Expose] = event_expose;
handlers[ClientMessage] = event_client_message;
handlers[PropertyNotify] = event_property_notify;
handlers[EnterNotify] = event_enter_notify;
handlers[LeaveNotify] = event_leave_notify;
}
/*
* handle an X event; this is exported to the world to allow pieces
* of code that have their own internal event loops (for instance,
* window movement) to call into the main handlers for certain
* events (such as exposure events).
*/
void event_handle(XEvent *e) {
plugin_t *plugin;
/*
* check event for plugin association; these events
* are dispatched to a plugin handler instead of getting
* handled here.
*/
if (!XFindContext(display, e->xany.window, plugin_context, (XPointer *) &plugin)) {
plugin_handle_event(plugin, e);
return;
}
/*
* if a handler exists in the table of event handlers,
* dispatch the event to it.
*/
if (e->type == shape_base + ShapeNotify)
event_shape_notify(e);
else if (handlers[e->type] != NULL)
handlers[e->type](e);
}
/*
* main loop for X event processing; when this routine
* exits the window manager will die.
*/
void event_loop() {
XEvent event;
event_init();
while (1) {
XNextEvent(display, &event);
event_handle(&event);
/*
* if something sets restart_flag, we leave this function
* and let main() take the appropriate action.
*/
if (restart_flag)
return;
}
}
syntax highlighted by Code2HTML, v. 0.9.1