/* Ratpoison X events
* Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca>
*
* This file is part of ratpoison.
*
* ratpoison 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, or (at your option)
* any later version.
*
* ratpoison 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 software; see the file COPYING. 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/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/Xmd.h> /* for CARD32. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/wait.h>
#include "ratpoison.h"
/* The event currently being processed. Mostly used in functions from
action.c which need to forward events to other windows. */
XEvent rp_current_event;
/* RAISED is non zero if a raised message should be used 0 for a map message. */
void
show_rudeness_msg (rp_window *win, int raised)
{
rp_group *g = groups_find_group_by_window (win);
rp_window_elem *elem = group_find_window (&g->mapped_windows, win);
if (g == rp_current_group)
{
if (win->transient)
marked_message_printf (0, 0, raised ? MESSAGE_RAISE_TRANSIENT:MESSAGE_MAP_TRANSIENT,
elem->number, window_name (win));
else
marked_message_printf (0, 0, raised ? MESSAGE_RAISE_WINDOW:MESSAGE_MAP_WINDOW,
elem->number, window_name (win));
}
else
{
if (win->transient)
marked_message_printf (0, 0, raised ? MESSAGE_RAISE_TRANSIENT_GROUP:MESSAGE_MAP_TRANSIENT_GROUP,
elem->number, window_name (win), g->name);
else
marked_message_printf (0, 0, raised ? MESSAGE_RAISE_WINDOW_GROUP:MESSAGE_MAP_WINDOW_GROUP,
elem->number, window_name (win), g->name);
}
}
static void
new_window (XCreateWindowEvent *e)
{
rp_window *win;
rp_screen *s;
if (e->override_redirect)
return;
win = find_window (e->window);
/* In Xinerama mode, all windows have the same root, so check
* all Xinerama screens
*/
if (rp_have_xinerama)
{
/* New windows belong to the current screen */
s = &screens[rp_current_screen];
}
else
{
s = find_screen (e->parent);
}
if (is_rp_window_for_screen(e->window, s)) return;
if (s && win == NULL
&& e->window != s->key_window
&& e->window != s->bar_window
&& e->window != s->input_window
&& e->window != s->frame_window
&& e->window != s->help_window)
{
win = add_to_window_list (s, e->window);
update_window_information (win);
}
}
static void
unmap_notify (XEvent *ev)
{
rp_frame *frame;
rp_window *win;
/* ignore SubstructureNotify unmaps. */
if(ev->xunmap.event != ev->xunmap.window
&& ev->xunmap.send_event != True)
return;
/* FIXME: Should we only look in the mapped window list? */
win = find_window_in_list (ev->xunmap.window, &rp_mapped_window);
if (win == NULL)
return;
switch (win->state)
{
case IconicState:
PRINT_DEBUG (("Withdrawing iconized window '%s'\n", window_name (win)));
if (ev->xunmap.send_event) withdraw_window (win);
break;
case NormalState:
PRINT_DEBUG (("Withdrawing normal window '%s'\n", window_name (win)));
/* If the window was inside a frame, fill the frame with another
window. */
frame = find_windows_frame (win);
if (frame)
{
cleanup_frame (frame);
if (frame->number == win->scr->current_frame
&& current_screen() == win->scr)
set_active_frame (frame, 0);
}
withdraw_window (win);
break;
}
update_window_names (win->scr, defaults.window_fmt);
}
static void
map_request (XEvent *ev)
{
rp_window *win;
win = find_window (ev->xmap.window);
if (win == NULL)
{
PRINT_DEBUG (("Map request from an unknown window.\n"));
XMapWindow (dpy, ev->xmap.window);
return;
}
PRINT_DEBUG (("Map request from a managed window\n"));
switch (win->state)
{
case WithdrawnState:
if (unmanaged_window (win->w))
{
PRINT_DEBUG (("Mapping Unmanaged Window\n"));
XMapWindow (dpy, win->w);
break;
}
else
{
PRINT_DEBUG (("Mapping Withdrawn Window\n"));
map_window (win);
break;
}
break;
case IconicState:
PRINT_DEBUG (("Mapping Iconic window\n"));
if (win->last_access == 0)
{
/* Depending on the rudeness level, actually map the
window. */
if ((rp_honour_transient_map && win->transient)
|| (rp_honour_normal_map && !win->transient))
set_active_window (win);
}
else
{
/* Depending on the rudeness level, actually map the
window. */
if ((rp_honour_transient_raise && win->transient)
|| (rp_honour_normal_raise && !win->transient))
set_active_window (win);
else
show_rudeness_msg (win, 1);
}
break;
}
}
static void
destroy_window (XDestroyWindowEvent *ev)
{
rp_window *win;
win = find_window (ev->window);
if (win == NULL) return;
ignore_badwindow++;
/* If, somehow, the window is not withdrawn before it is destroyed,
perform the necessary steps to withdraw the window before it is
unmanaged. */
if (win->state == IconicState)
{
PRINT_DEBUG (("Destroying Iconic Window (%s)\n", window_name (win)));
withdraw_window (win);
}
else if (win->state == NormalState)
{
rp_frame *frame;
PRINT_DEBUG (("Destroying Normal Window (%s)\n", window_name (win)));
frame = find_windows_frame (win);
if (frame)
{
cleanup_frame (frame);
if (frame->number == win->scr->current_frame
&& current_screen() == win->scr)
set_active_frame (frame, 0);
}
withdraw_window (win);
}
/* Now that the window is guaranteed to be in the unmapped window
list, we can safely stop managing it. */
unmanage (win);
ignore_badwindow--;
}
static void
configure_request (XConfigureRequestEvent *e)
{
XWindowChanges changes;
rp_window *win;
win = find_window (e->window);
if (win)
{
if (e->value_mask & CWStackMode)
{
if (e->detail == Above && win->state != WithdrawnState)
{
/* Depending on the rudeness level, actually map the
window. */
if ((rp_honour_transient_raise && win->transient)
|| (rp_honour_normal_raise && !win->transient))
{
if (win->state == IconicState)
set_active_window (win);
else if (find_windows_frame (win))
goto_window (win);
}
else if (current_window() != win)
{
show_rudeness_msg (win, 1);
}
}
PRINT_DEBUG(("request CWStackMode %d\n", e->detail));
}
PRINT_DEBUG (("'%s' window size: %d %d %d %d %d\n", window_name (win),
win->x, win->y, win->width, win->height, win->border));
/* Collect the changes to be granted. */
if (e->value_mask & CWBorderWidth)
{
changes.border_width = e->border_width;
win->border = e->border_width;
PRINT_DEBUG(("request CWBorderWidth %d\n", e->border_width));
}
if (e->value_mask & CWWidth)
{
changes.width = e->width;
win->width = e->width;
PRINT_DEBUG(("request CWWidth %d\n", e->width));
}
if (e->value_mask & CWHeight)
{
changes.height = e->height;
win->height = e->height;
PRINT_DEBUG(("request CWHeight %d\n", e->height));
}
if (e->value_mask & CWX)
{
changes.x = e->x;
win->x = e->x;
PRINT_DEBUG(("request CWX %d\n", e->x));
}
if (e->value_mask & CWY)
{
changes.y = e->y;
win->y = e->y;
PRINT_DEBUG(("request CWY %d\n", e->y));
}
if (e->value_mask & (CWX|CWY|CWBorderWidth|CWWidth|CWHeight))
{
/* Grant the request, then immediately maximize it. */
XConfigureWindow (dpy, win->w,
e->value_mask & (CWX|CWY|CWBorderWidth|CWWidth|CWHeight),
&changes);
XSync(dpy, False);
if (win->state == NormalState)
maximize (win);
}
}
else
{
/* Its an unmanaged window, so give it what it wants. But don't
change the stack mode.*/
if (e->value_mask & CWX) changes.x = e->x;
if (e->value_mask & CWY) changes.x = e->x;
if (e->value_mask & CWWidth) changes.x = e->x;
if (e->value_mask & CWHeight) changes.x = e->x;
if (e->value_mask & CWBorderWidth) changes.x = e->x;
XConfigureWindow (dpy, e->window,
e->value_mask & (CWX|CWY|CWBorderWidth|CWWidth|CWHeight),
&changes);
}
}
static void
client_msg (XClientMessageEvent *ev)
{
PRINT_DEBUG (("Received client message.\n"));
if (ev->message_type == wm_change_state)
{
rp_window *win;
PRINT_DEBUG (("WM_CHANGE_STATE\n"));
win = find_window (ev->window);
if (win == NULL) return;
if (ev->format == 32 && ev->data.l[0] == IconicState)
{
/* FIXME: This means clients can hide themselves without the
user's intervention. This is bad, but Emacs is the only
program I know of that iconifies itself and this is
generally from the user pressing C-z. */
PRINT_DEBUG (("Iconify Request.\n"));
if (win->state == NormalState)
{
rp_window *w = find_window_other(win->scr);
if (w)
set_active_window (w);
else
blank_frame (screen_get_frame (win->scr, win->scr->current_frame));
}
}
else
{
PRINT_ERROR (("Non-standard WM_CHANGE_STATE format\n"));
}
}
}
static void
handle_key (KeySym ks, unsigned int mod, rp_screen *s)
{
rp_action *key_action;
rp_keymap *map = find_keymap (defaults.top_kmap);
if (map == NULL)
{
PRINT_ERROR (("Unable to find %s keymap\n", defaults.top_kmap));
return;
}
PRINT_DEBUG (("handling key...\n"));
/* All functions hide the program bar and the frame indicator. */
if (defaults.bar_timeout > 0) hide_bar (s);
hide_frame_indicator();
/* Disable any alarm that was going to go off. */
alarm (0);
alarm_signalled = 0;
/* Call the top level key pressed hook. */
hook_run (&rp_key_hook);
PRINT_DEBUG (("handle_key\n"));
/* Read a key and execute the command associated with it on the
default keymap. Ignore the key if it doesn't have a binding. */
if ((key_action = find_keybinding (ks, x11_mask_to_rp_mask (mod), map)))
{
cmdret *result;
PRINT_DEBUG(("%s\n", key_action->data));
result = command (1, key_action->data);
if (result)
{
if (result->output)
message (result->output);
cmdret_free (result);
}
}
else
{
PRINT_DEBUG(("Impossible: No matching key"));
}
XAllowEvents (dpy, AsyncKeyboard, CurrentTime);
}
static void
key_press (XEvent *ev)
{
rp_screen *s;
unsigned int modifier;
KeySym ks;
if (rp_have_xinerama)
s = current_screen();
else
s = find_screen (ev->xkey.root);
#ifdef HIDE_MOUSE
XWarpPointer (dpy, None, s->root, 0, 0, 0, 0, s->left + s->width - 2, s->top + s->height - 2);
#endif
if (!s) return;
modifier = ev->xkey.state;
cook_keycode ( &ev->xkey, &ks, &modifier, NULL, 0, 1);
handle_key (ks, modifier, s);
}
/* Read a command off the window and execute it. Some commands return
text. This text is passed back using the RP_COMMAND_RESULT
Atom. The client will wait for this property change so something
must be returned. */
static cmdret *
execute_remote_command (Window w)
{
int status;
cmdret *ret;
Atom type_ret;
int format_ret;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *req;
status = XGetWindowProperty (dpy, w, rp_command,
0, 0, False, XA_STRING,
&type_ret, &format_ret, &nitems, &bytes_after,
&req);
if (status != Success || req == NULL)
{
return cmdret_new (RET_FAILURE, "Couldn't get RP_COMMAND Property");
}
/* XGetWindowProperty always allocates one extra byte even if
the property is zero length. */
XFree (req);
status = XGetWindowProperty (dpy, w, rp_command,
0, (bytes_after / 4) + (bytes_after % 4 ? 1 : 0),
True, XA_STRING, &type_ret, &format_ret, &nitems,
&bytes_after, &req);
if (status != Success || req == NULL)
{
return cmdret_new (RET_FAILURE, "Couldn't get RP_COMMAND Property");
}
PRINT_DEBUG (("command: %s\n", req));
ret = command (req[0], (char *)&req[1]);
XFree (req);
return ret;
}
/* Command requests are posted as a property change using the
RP_COMMAND_REQUEST Atom on the root window. A Command request is a
Window that holds the actual command as a property using the
RP_COMMAND Atom. receive_command reads the list of Windows and
executes their associated command. */
static void
receive_command (Window root)
{
cmdret *cmd_ret;
char *result;
Atom type_ret;
int format_ret;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *prop_return;
int offset;
/* Init offset to 0. In the case where there is more than one window
in the property, a partial read does not delete the property and
we need to grab the next window by incementing offset to the
offset of the next window. */
offset = 0;
do
{
int ret;
int length;
Window w;
length = sizeof (Window) / 4 + (sizeof (Window) % 4 ?1:0);
ret = XGetWindowProperty (dpy, root,
rp_command_request,
offset, length,
True, XA_WINDOW, &type_ret, &format_ret,
&nitems,
&bytes_after, &prop_return);
/* Update the offset to point to the next window (if there is
another one). */
offset += length;
if (ret != Success)
{
PRINT_ERROR (("XGetWindowProperty Failed\n"));
if (prop_return)
XFree (prop_return);
break;
}
/* If there was no window, then we're done. */
if (prop_return == NULL)
{
PRINT_DEBUG (("No property to read\n"));
break;
}
/* We grabbed a window, so now read the command stored in
this window and execute it. */
w = *(Window *)prop_return;
XFree (prop_return);
cmd_ret = execute_remote_command (w);
/* notify the client of any text that was returned by the
command. */
if (cmd_ret->output)
result = xsprintf ("%c%s", cmd_ret->success ? '1':'0', cmd_ret->output);
else
result = NULL;
if (result)
XChangeProperty (dpy, w, rp_command_result, XA_STRING,
8, PropModeReplace, (unsigned char *)result, strlen (result));
else
XChangeProperty (dpy, w, rp_command_result, XA_STRING,
8, PropModeReplace, NULL, 0);
free (result);
cmdret_free (cmd_ret);
} while (bytes_after > 0);
}
static void
property_notify (XEvent *ev)
{
rp_window *win;
PRINT_DEBUG (("atom: %ld\n", ev->xproperty.atom));
if (ev->xproperty.atom == rp_command_request
&& is_a_root_window (ev->xproperty.window)
&& ev->xproperty.state == PropertyNewValue)
{
PRINT_DEBUG (("ratpoison command\n"));
receive_command(ev->xproperty.window);
}
win = find_window (ev->xproperty.window);
if (win)
{
if (ev->xproperty.atom == _net_wm_pid)
{
struct rp_child_info *child_info;
PRINT_DEBUG (("updating _NET_WM_PID\n"));
child_info = get_child_info(win->w);
if (child_info && !child_info->window_mapped)
{
if (child_info->frame)
{
PRINT_DEBUG (("frame=%p\n", child_info->frame));
win->intended_frame_number = child_info->frame->number;
/* Only map the first window in the launch frame. */
child_info->window_mapped = 1;
}
/* TODO: also adopt group information? */
}
} else
switch (ev->xproperty.atom)
{
case XA_WM_NAME:
PRINT_DEBUG (("updating window name\n"));
if (update_window_name (win))
update_window_names (win->scr, defaults.window_fmt);
break;
case XA_WM_NORMAL_HINTS:
PRINT_DEBUG (("updating window normal hints\n"));
update_normal_hints (win);
if (win->state == NormalState)
maximize (win);
break;
case XA_WM_TRANSIENT_FOR:
PRINT_DEBUG (("Transient for\n"));
win->transient = XGetTransientForHint (dpy, win->w, &win->transient_for);
break;
default:
PRINT_DEBUG (("Unhandled property notify event: %ld\n", ev->xproperty.atom));
break;
}
}
}
static void
colormap_notify (XEvent *ev)
{
rp_window *win;
win = find_window (ev->xcolormap.window);
if (win != NULL)
{
XWindowAttributes attr;
/* SDL sets the colormap just before destroying the window, so
ignore BadWindow errors. */
ignore_badwindow++;
XGetWindowAttributes (dpy, win->w, &attr);
win->colormap = attr.colormap;
if (win == current_window()
&& !current_screen()->bar_is_raised)
{
XInstallColormap (dpy, win->colormap);
}
ignore_badwindow--;
}
}
static void
focus_change (XFocusChangeEvent *ev)
{
rp_window *win;
/* We're only interested in the NotifyGrab mode */
if (ev->mode != NotifyGrab) return;
win = find_window (ev->window);
if (win != NULL)
{
PRINT_DEBUG (("Re-grabbing prefix key\n"));
grab_top_level_keys (win->w);
}
}
static void
mapping_notify (XMappingEvent *ev)
{
ungrab_keys_all_wins();
switch (ev->request)
{
case MappingModifier:
update_modifier_map();
/* This is meant to fall through. */
case MappingKeyboard:
XRefreshKeyboardMapping (ev);
break;
}
grab_keys_all_wins();
}
static void
configure_notify (XConfigureEvent *ev)
{
rp_screen *s;
s = find_screen(ev->window);
if (s != NULL)
/* This is a root window of a screen,
* look if its width or height changed: */
screen_update(s,ev->width,ev->height);
}
/* This is called whan an application has requested the
selection. Copied from rxvt. */
static void
selection_request (XSelectionRequestEvent *rq)
{
XEvent ev;
CARD32 target_list[4];
Atom target;
static Atom xa_targets = None;
static Atom xa_compound_text = None;
static Atom xa_text = None;
XTextProperty ct;
XICCEncodingStyle style;
char *cl[4];
if (xa_text == None)
xa_text = XInternAtom(dpy, "TEXT", False);
if (xa_compound_text == None)
xa_compound_text = XInternAtom(dpy, "COMPOUND_TEXT", False);
if (xa_targets == None)
xa_targets = XInternAtom(dpy, "TARGETS", False);
ev.xselection.type = SelectionNotify;
ev.xselection.property = None;
ev.xselection.display = rq->display;
ev.xselection.requestor = rq->requestor;
ev.xselection.selection = rq->selection;
ev.xselection.target = rq->target;
ev.xselection.time = rq->time;
if (rq->target == xa_targets) {
target_list[0] = (CARD32) xa_targets;
target_list[1] = (CARD32) XA_STRING;
target_list[2] = (CARD32) xa_text;
target_list[3] = (CARD32) xa_compound_text;
XChangeProperty(dpy, rq->requestor, rq->property, rq->target,
(8 * sizeof(target_list[0])), PropModeReplace,
(unsigned char *)target_list,
(sizeof(target_list) / sizeof(target_list[0])));
ev.xselection.property = rq->property;
} else if (rq->target == XA_STRING
|| rq->target == xa_compound_text
|| rq->target == xa_text) {
if (rq->target == XA_STRING) {
style = XStringStyle;
target = XA_STRING;
} else {
target = xa_compound_text;
style = (rq->target == xa_compound_text) ? XCompoundTextStyle
: XStdICCTextStyle;
}
cl[0] = selection.text;
XmbTextListToTextProperty(dpy, cl, 1, style, &ct);
XChangeProperty(dpy, rq->requestor, rq->property,
target, 8, PropModeReplace,
ct.value, ct.nitems);
ev.xselection.property = rq->property;
}
XSendEvent(dpy, rq->requestor, False, 0, &ev);
}
static void
selection_clear (void)
{
if (selection.text)
free (selection.text);
selection.text = NULL;
selection.len = 0;
}
/* Given an event, call the correct function to handle it. */
static void
delegate_event (XEvent *ev)
{
switch (ev->type)
{
case ConfigureRequest:
PRINT_DEBUG (("--- Handling ConfigureRequest ---\n"));
configure_request (&ev->xconfigurerequest);
break;
case CreateNotify:
PRINT_DEBUG (("--- Handling CreateNotify ---\n"));
new_window (&ev->xcreatewindow);
break;
case DestroyNotify:
PRINT_DEBUG (("--- Handling DestroyNotify ---\n"));
destroy_window (&ev->xdestroywindow);
break;
case ClientMessage:
PRINT_DEBUG (("--- Handling ClientMessage ---\n"));
client_msg (&ev->xclient);
break;
case ColormapNotify:
PRINT_DEBUG (("--- Handling ColormapNotify ---\n"));
colormap_notify (ev);
break;
case PropertyNotify:
PRINT_DEBUG (("--- Handling PropertyNotify ---\n"));
property_notify (ev);
break;
case MapRequest:
PRINT_DEBUG (("--- Handling MapRequest ---\n"));
map_request (ev);
break;
case KeyPress:
PRINT_DEBUG (("--- Handling KeyPress ---\n"));
key_press (ev);
break;
case UnmapNotify:
PRINT_DEBUG (("--- Handling UnmapNotify ---\n"));
unmap_notify (ev);
break;
case FocusOut:
PRINT_DEBUG (("--- Handling FocusOut ---\n"));
focus_change (&ev->xfocus);
break;
case FocusIn:
PRINT_DEBUG (("--- Handling FocusIn ---\n"));
focus_change (&ev->xfocus);
break;
case MappingNotify:
PRINT_DEBUG (("--- Handling MappingNotify ---\n"));
mapping_notify( &ev->xmapping );
break;
case SelectionRequest:
selection_request(&ev->xselectionrequest);
break;
case SelectionClear:
selection_clear();
break;
case ConfigureNotify:
PRINT_DEBUG (("--- Handling ConfigureNotify ---\n"));
configure_notify( &ev->xconfigure );
break;
case MapNotify:
case Expose:
case MotionNotify:
case KeyRelease:
case ReparentNotify:
case EnterNotify:
case SelectionNotify:
case CirculateRequest:
/* Ignore these events. */
break;
default:
PRINT_DEBUG (("--- Unknown event %d ---\n",- ev->type));
}
}
static void
handle_signals (void)
{
/* An alarm means we need to hide the popup windows. */
if (alarm_signalled > 0)
{
int i;
PRINT_DEBUG (("Alarm received.\n"));
/* Only hide the bar if it times out. */
if (defaults.bar_timeout > 0)
for (i=0; i<num_screens; i++)
hide_bar (&screens[i]);
hide_frame_indicator();
alarm_signalled = 0;
}
if (chld_signalled > 0)
{
rp_child_info *cur;
struct list_head *iter, *tmp;
/* Report and remove terminated processes. */
list_for_each_safe_entry (cur, iter, tmp, &rp_children, node)
{
if (cur->terminated)
{
/* Report any child that didn't return 0. */
if (cur->status != 0)
marked_message_printf (0,0, "/bin/sh -c \"%s\" finished (%d)",
cur->cmd, cur->status);
list_del (&cur->node);
free (cur->cmd);
free (cur);
}
}
chld_signalled = 0;
}
if (rp_exec_newwm)
{
PRINT_DEBUG (("Switching to %s\n", rp_exec_newwm));
putenv(current_screen()->display_string);
unhide_all_windows();
XSync(dpy, False);
execlp(rp_exec_newwm, rp_exec_newwm, NULL);
/* Failed. Clean up. */
PRINT_ERROR (("exec %s ", rp_exec_newwm));
perror(" failed");
free (rp_exec_newwm);
rp_exec_newwm = NULL;
}
if (hup_signalled > 0)
{
PRINT_DEBUG (("Restarting\n"));
hook_run (&rp_restart_hook);
clean_up ();
execvp(myargv[0], myargv);
}
if (kill_signalled > 0)
{
PRINT_DEBUG (("Exiting\n"));
hook_run (&rp_quit_hook);
clean_up ();
exit (EXIT_SUCCESS);
}
/* Report any X11 errors that have occurred. */
if (rp_error_msg)
{
marked_message_printf (0, 6, "ERROR: %s", rp_error_msg);
free (rp_error_msg);
rp_error_msg = NULL;
}
}
/* The main loop. */
void
listen_for_events (void)
{
int x_fd;
fd_set fds;
x_fd = ConnectionNumber (dpy);
FD_ZERO (&fds);
/* Loop forever. */
for (;;)
{
handle_signals ();
/* Handle the next event. */
FD_SET (x_fd, &fds);
XFlush(dpy);
if (QLength (dpy) > 0
|| select(x_fd+1, &fds, NULL, NULL, NULL) == 1)
{
XNextEvent (dpy, &rp_current_event);
delegate_event (&rp_current_event);
XSync(dpy, False);
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1