/* x-input.m -- event handling
   $Id: x-input.m,v 1.26 2007-04-07 20:39:03 jharper Exp $

   Copyright (c) 2002 Apple Computer, Inc. All rights reserved. */

#include "quartz-wm.h"
#import "x-screen.h"
#import "x-selection.h"
#import "x-window.h"
#include "frame.h"
#include "utils.h"

#include <CoreFoundation/CFSocket.h>
#include <CoreFoundation/CFRunLoop.h>
#include <HIServices/CoreDockServicesPriv.h>

#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/extensions/applewm.h>

#include <unistd.h>

extern BOOL _proxy_pb;

/* FIXME: .. */
#define DOUBLE_CLICK_TIME 250

#define BUTTON_MASK \
    (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)

CFRunLoopSourceRef x_dpy_source;

static struct {
    Window down_id;
    Time down_time;			/* milliseconds */
    CGPoint down_location;
    CGPoint offset;
    int click_count;
    unsigned int down_attrs;
    unsigned dragging :1;
    unsigned clicking :1;
    unsigned resizing :1;
} pointer;

/* Timestamp when the X server last told us it's active */
static Time last_activation_time;

static float
point_distance (CGPoint a, CGPoint b)
{
    float dx, dy;

    dx = b.x - a.x;
    dy = b.y - a.y;

    return sqrt (dx * dx + dy * dy);
}

static inline int
count_bits (uint32_t x)
{
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = (x + (x >> 4)) & 0x0f0f0f0f;
    x = x + (x >> 8);
    x = x + (x >> 16);
    return x & 63;
}

static inline int
buttons_pressed (unsigned int state)
{
    return count_bits (state & BUTTON_MASK);
}

static void *
list_next (x_list *lst, void *item)
{
    x_list *node;

    node = x_list_find (lst, item);

    if (node != NULL)
    {
	node = node->next;
	if (node == NULL)
	    node = lst;
    }

    if (node != NULL && node->data != item)
	return node->data;

    return NULL;
}

static void
next_window (Time time, Bool reversed)
{
    x_window *w, *x;
    x_list *lst;

    w = x_get_active_window ();
    if (w != nil)
    {
	lst = x_list_copy (w->_screen->_window_list);
	if (reversed)
	    lst = x_list_reverse (lst);

	x = list_next (lst, w);

	/* Skip minimized windows. */
	while (x != nil && x != w && x->_minimized)
	    x = list_next (lst, x);

	[x activate:time];

	x_list_free (lst);
    }
}

static void
x_event_button (XButtonEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (w == nil)
	return;

    if (e->window == w->_id)
    {
	/* Swallow the first activating click. Since the X server activates
	   us we need to look at timestamps to handle the case where the
	   user activated X by clicking on the already focused window. Use
	   a 100ms window for this. */

	if ((!w->_focused || (e->time - last_activation_time) < 100)
	    && [w focus:e->time])
	{
	    if (!w->_click_through)
	    {
		XAllowEvents (x_dpy, AsyncBoth, e->time);
	    }
	    else
	    {
		XAllowEvents (x_dpy, ReplayPointer, e->time);
		XUngrabPointer (x_dpy, e->time);
	    }
	}
	else
	{
	    [w raise];
	    XAllowEvents (x_dpy, ReplayPointer, e->time);
	    XUngrabPointer (x_dpy, e->time);
	}
    }
    else if (e->button <= 3
	     && (e->window == w->_frame_id || e->window == w->_growbox_id))
    {
	unsigned int old_attrs = w->_frame_attr;
	CGPoint p = CGPointMake (e->x, e->y);

	if (e->window == w->_growbox_id)
	{
	    p.x += w->_growbox_rect.origin.x;
	    p.y += w->_growbox_rect.origin.y;
	}

	if (e->type == ButtonPress)
	{
	    /* FIXME: double click on title bar should collapse window */

	    if (buttons_pressed (e->state) == 0)
	    {
		/* First button press */

	        /* FIXME: wrap around? */
		if (e->time - pointer.down_time < DOUBLE_CLICK_TIME)
		    pointer.click_count++;
		else
		    pointer.click_count = 1;

		pointer.down_location.x = e->x_root;
		pointer.down_location.y = e->y_root;
		pointer.down_id = e->window;
		pointer.down_time = e->time;
		pointer.down_attrs = [w hit_test_frame:p];

		if ((pointer.down_attrs
		     & (w->_frame_attr & XP_FRAME_ANY_BUTTON)) != 0)
		{
		    pointer.clicking = YES;
		    XP_FRAME_ATTR_SET_CLICKED (w->_frame_attr,
					    pointer.down_attrs
					    & XP_FRAME_ANY_BUTTON);
		}
		else
		{
		    [w focus:e->time raise:!(e->state & x_meta_mod)];
		}
	    }
	}
	else if (e->type == ButtonRelease)
	{
	    if (buttons_pressed (e->state) == 1)
	    {
		/* Releasing last button */

		if (pointer.dragging)
		{
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
            CoreDockSendDragWindowMessage(kCoreDockWindowDragEnded, [w get_cg_id]);
#endif
		    pointer.dragging = NO;
		}
		else if (pointer.resizing)
		{
		    pointer.resizing = NO;
		    [w remove_resizing_title];
		}
		else if (pointer.clicking)
		{
		    unsigned int attrs;

            /* Test the release location */
		    attrs = [w hit_test_frame:p];
            
            /* Only if it went pressed the same button it came released on */
            attrs &= pointer.down_attrs;

		    if (attrs & XP_FRAME_CLOSE_BOX)
			[w do_close:e->time];
		    else if (attrs & XP_FRAME_COLLAPSE)
			[w do_collapse];
		    else if (attrs & XP_FRAME_ZOOM)
			[w do_zoom];
		    
		    XP_FRAME_ATTR_UNSET_CLICKED (w->_frame_attr,
					      XP_FRAME_ANY_BUTTON);

		    /* Update prelight bit, we ignored tracking events
		       while clickiing. */
		    w->_frame_attr &= ~XP_FRAME_PRELIGHT;
		    w->_frame_attr |= attrs & XP_FRAME_PRELIGHT;

		    pointer.clicking = NO;
		}
		else if (pointer.click_count == 2)
		{
		    if (w->_shadable)
			[w do_toggle_shaded:e->time];
		    else
			[w do_collapse];
		}

		pointer.down_id = 0;
	    }
	}

	if (w->_frame_attr != old_attrs)
	{
	    [w decorate];
	}
    }
}

static void
x_event_motion_notify (XMotionEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (w == nil)
	return;

#if 0
    if (e->window == w->_id)
    {
	XAllowEvents (x_dpy, ReplayPointer, e->time);
	XUngrabPointer (x_dpy, e->time);
    }
    else
#endif
    if (e->window == w->_frame_id || e->window == w->_growbox_id)
    {
	CGPoint p;
	CGRect r;
	unsigned int old_attrs = w->_frame_attr, attrs;

	Window tem_w;
	unsigned int tem_i;
	int x, y, wx, wy;

	XQueryPointer (x_dpy, e->window, &tem_w, &tem_w,
		       &x, &y, &wx, &wy, &tem_i);
	p = CGPointMake (x, y);

	if (buttons_pressed (e->state) == 0)
	{
	    /* We must have missed the button-release */
        if(pointer.dragging) {
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
            CoreDockSendDragWindowMessage(kCoreDockWindowDragEnded, [w get_cg_id]);
#endif
            pointer.dragging = NO;
        }
        if (pointer.resizing) {
            pointer.resizing = NO;
            [w remove_resizing_title];
        }
	}

	if (pointer.dragging)
	{
	do_drag:
	    r = CGRectMake(x + pointer.offset.x, y + pointer.offset.y,
                       w->_current_frame.size.width,
                       w->_current_frame.size.height);
	    r.origin = [w->_screen validate_window_position:r titlebar_height:w->_frame_title_height];
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
	    CoreDockSendDragWindowMessage(kCoreDockWindowDragStarted, [w get_cg_id]);
#endif
	    [w resize_frame:r];
	}
	else if (pointer.resizing)
	{
	do_resize:
	    r = CGRectMake (w->_current_frame.origin.x, w->_current_frame.origin.y,
			    pointer.offset.x + (p.x - pointer.down_location.x),
			    pointer.offset.y + (p.y - pointer.down_location.y));
	    if (r.size.width > 0 && r.size.height > 0)
	    {
		r = [w validate_frame_rect:r from_user:YES];
		[w resize_frame:r];
		[w set_resizing_title:r];
	    }
	}
	else if (pointer.clicking)
	{
	    attrs = [w hit_test_frame:CGPointMake (wx, wy)];

	    if ((attrs & XP_FRAME_ANY_BUTTON)
		!= (pointer.down_attrs & XP_FRAME_ANY_BUTTON))
	    {
		/* Moved off the button we clicked on, change its state. */

		XP_FRAME_ATTR_UNSET_CLICKED (w->_frame_attr, XP_FRAME_ANY_BUTTON);
	    }
	    else
	    {
		XP_FRAME_ATTR_SET_CLICKED (w->_frame_attr, 
					pointer.down_attrs
					& XP_FRAME_ANY_BUTTON);
	    }
	}
	else
	{
	    /* See if we moved far enough to start dragging the window. */

	    if ((e->state & BUTTON_MASK) != 0
		&& point_distance (pointer.down_location, p) >= DRAG_THRESHOLD)
	    {
		if (e->window == w->_growbox_id)
		{
		    pointer.offset.x = w->_current_frame.size.width;
		    pointer.offset.y = w->_current_frame.size.height;
		    pointer.resizing = YES;
		    goto do_resize;
		}
		else if (e->window == w->_frame_id
			 && w->_movable && e->subwindow == None)
		{
		    pointer.offset.x = w->_current_frame.origin.x - pointer.down_location.x;
		    pointer.offset.y = w->_current_frame.origin.y - pointer.down_location.y;
		    pointer.dragging = YES;
		    goto do_drag;
		}
	    }
	}

	if (w->_frame_attr != old_attrs)
	{
	    [w decorate];
	}
    }
}

static void
x_event_key (XKeyEvent *e)
{
    KeySym keysym;

    if (e->type != KeyPress)
	return;

    keysym = XKeycodeToKeysym (x_dpy, e->keycode,
			       e->state & ShiftMask ? 1 : 0);

    switch (keysym)
    {
    case XK_grave:
	next_window (e->time, FALSE);
	break;

    default:
	XAllowEvents (x_dpy, ReplayKeyboard, e->time);
	XUngrabKeyboard (x_dpy, e->time);
	return;
    }

    XAllowEvents (x_dpy, AsyncBoth, e->time);
}

static void
x_event_property_notify (XPropertyEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (w == nil || e->window != w->_id)
	return;

    [w property_changed:e->atom];
}

static void x_event_client_message (XClientMessageEvent *e) {
    x_window *w = x_get_window(e->window);

    if (w == nil || e->format != 32)
        return;

    if(e->message_type == atoms.wm_change_state) {
        if (e->data.l[0] == IconicState)
            [w do_collapse];
    } else if(e->message_type == atoms.net_active_window) {
        [w focus:x_current_timestamp ()];
    } else if(e->message_type == atoms.net_close_window) {
        [w do_close:x_current_timestamp ()];
    } else if(e->message_type == atoms.net_wm_state) {
        if (e->data.l[1] != 0)
            [w do_net_wm_state_change:e->data.l[0] atom:e->data.l[1]];
        if (e->data.l[2] != 0)
            [w do_net_wm_state_change:e->data.l[0] atom:e->data.l[2]];
    }
}

static void
x_event_destroy_notify (XDestroyWindowEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (w == nil || e->window != w->_id)
	return;

    [w->_screen remove_window:w];
}

static void
x_event_map_request (XMapRequestEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (w == nil)
    {
	XWindowAttributes attr;
	x_screen *s;

	XGetWindowAttributes (x_dpy, e->window, &attr);
	s = x_get_screen (attr.screen);
	if (s == nil)
	    return;

	[s adopt_window:e->window initializing:NO];
    }
    else
    {
	/* Remapping a window is the signal to make it become non-iconic. */

	w->_unmapped = NO;
	[w activate:CurrentTime];
    }
}

static void
x_event_reparent_notify (XReparentEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (w == nil || w->_id != e->window || w->_id != e->event)
	return;

    [w->_screen remove_window:w];

    XReparentWindow (x_dpy, e->window, e->parent, e->x, e->y);
}

static void
x_event_unmap_notify (XUnmapEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (w == nil || w->_id != e->window
	|| (e->event != w->_id && !e->send_event))
    {
	return;
    }

    w->_unmapped = YES;
    [w->_screen remove_window:w];

    XDeleteProperty (x_dpy, e->window, atoms.wm_state);
}

static void
x_event_focus (XFocusChangeEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (e->detail == NotifyPointer || w == nil)
	return;

    if (e->type == FocusIn)
    {
	[w x_focus_in];
    }
    else if (e->type == FocusOut)
    {
	if (e->detail != NotifyInferior)
	{
	    [w x_focus_out];
	}
    }
}

static void
x_event_crossing (XCrossingEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (w == nil)
	return;

    if (e->window == w->_tracking_id)
    {
	if (pointer.clicking)
	    return;

	if (e->type == EnterNotify)
	    w->_frame_attr |= XP_FRAME_PRELIGHT;
	else if (e->type == LeaveNotify)
	    w->_frame_attr &= ~XP_FRAME_PRELIGHT;

	[w decorate];
    }
    else if (e->window == w->_frame_id && focus_follows_mouse)
    {
	if (e->type == EnterNotify)
	    [w focus:e->time raise:NO];
    }
}

static void
x_event_configure_notify (XConfigureEvent *e)
{
    x_window *w;
    x_screen *s;

    if (e->send_event)
	return;

    w = x_get_window (e->window);

    if (w != nil)
    {
	CGRect r = CGRectMake (e->x, e->y, e->width, e->height);

	if (e->window == w->_frame_id)
	{
	    [w report_frame_size:r];
	}
    }
    else
    {
	s = x_get_screen_with_root (e->window);

	if (s != nil)
	{
	    [s update_geometry];
	    [s foreach_window:@selector (validate_position)];
	}
    }
}

static void x_event_configure_request(XConfigureRequestEvent *e) {
    x_window *w = x_get_window (e->window);
    
    XWindowChanges client_changes, real_frame_changes, *frame_changes;
    unsigned long client_mask, real_frame_mask, *frame_mask;
    
    if(w != nil && e->window != w->_id)
        w = nil;
    
    if(w == nil) {
        /* whatever.. */
        
        client_changes.stack_mode = e->detail;
        client_changes.sibling = e->above;
        client_changes.x = e->x;
        client_changes.y = e->y;
        client_changes.width = e->width;
        client_changes.height = e->height;
        
        XConfigureWindow (x_dpy, e->window, e->value_mask, &client_changes);
        
        return;
    }
    
    if(e->value_mask & CWBorderWidth)
        w->_xattr.border_width = e->border_width;
    
    //[w update_shaped];
    
    client_mask = real_frame_mask = 0;
    
    frame_changes = w->_reparented ? &real_frame_changes : &client_changes;
    frame_mask = w->_reparented ? &real_frame_mask : &client_mask;
    
    if (e->value_mask & CWStackMode) {
        frame_changes->stack_mode = e->detail;
        *frame_mask |= CWStackMode;
        
        if(e->value_mask & CWSibling) {
            frame_changes->sibling = e->above;
            *frame_mask |= CWSibling;
        }
    }
    
    if(e->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
        CGRect client_rect = CGRectMake(w->_xattr.x, w->_xattr.y,
                                        w->_xattr.width, w->_xattr.height);
        if (e->value_mask & CWX)
            client_rect.origin.x = e->x;
        if (e->value_mask & CWY)
            client_rect.origin.y = e->y;
        if (e->value_mask & CWWidth)
            client_rect.size.width = e->width;
        if (e->value_mask & CWHeight)
            client_rect.size.height = e->height;
        
        [w resize_client:client_rect];
    }
    
    if(real_frame_mask) {
        XConfigureWindow(x_dpy, w->_frame_id,
                         real_frame_mask, &real_frame_changes);
    }
    
    if(client_mask) {
        XConfigureWindow(x_dpy, w->_id, client_mask, &client_changes);
    }
}

static void
x_event_shape_notify (XShapeEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (w == nil || w->_id != e->window || e->kind != ShapeBounding)
	return;

    w->_shaped = YES;

    [w update_shape];
}

static void
x_event_expose (XExposeEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (w == nil || w->_frame_id != e->window)
	return;

    if (e->count == 0)
	[w expose];
}

static void
x_event_colormap_notify (XColormapEvent *e)
{
    x_window *w = x_get_window (e->window);

    if (w == nil || w->_id != e->window || e->new == 0)
	return;

    if (w->_focused)
	[w install_colormaps];
}

static void
x_event_mapping_notify (XMappingEvent *e)
{
    x_update_keymap ();
}

static void
x_event_apple_wm_notify (XAppleWMNotifyEvent *e)
{
    static const char *controller_kinds[] = {
	"Minimizewindow", "ZoomWindow", "CloseWindow",
	"BringAllToFront", "HideWindow", "HideAll", "ShowAll",
	"Unknown", "Unknown", "WindowMenuItem", "WindowMenuNotify",
	"NextWindow", "PreviousWindow",
    };

    static const char *activation_kinds[] = {
	"IsActive", "IsInactive", "ReloadPreferences"
    };

    static const char *pasteboard_kinds[] = {
	"CopyToPasteboard",
    };

    switch (e->type - x_apple_wm_event_base)
    {
    case AppleWMControllerNotify:
	if (e->kind >= 0 && e->kind < (int) (sizeof (controller_kinds)
					     / sizeof (controller_kinds[0])))
	{
	    DB("kind %s arg %d\n", controller_kinds[e->kind], e->arg);
	}
	else
	{
	    DB("kind %d arg %d\n", e->kind, e->arg);
	}

	switch (e->kind)
	{
	    x_window *w;

	case AppleWMMinimizeWindow:
	case AppleWMZoomWindow:
	case AppleWMCloseWindow:
	case AppleWMHideWindow:
	    w = x_get_active_window ();
	    if (w != nil)
	    {
		if (e->kind == AppleWMMinimizeWindow)
		    [w do_collapse];
		else if (e->kind == AppleWMZoomWindow)
		    [w do_zoom];
		else if (e->kind == AppleWMCloseWindow)
		    [w do_close:e->time];
		else if (e->kind == AppleWMHideWindow)
		    [w do_hide];
	    }
	    break;

	case AppleWMBringAllToFront:
	    x_show_all (e->time, NO);
	    x_bring_all_to_front (e->time);
	    break;

	case AppleWMHideAll:
	    x_hide_all (e->time);
	    break;

	case AppleWMShowAll:
	    x_show_all (e->time, NO);
	    break;

	case AppleWMWindowMenuItem:
	    x_activate_window_in_menu (e->arg, e->time);
	    break;

	case AppleWMWindowMenuNotify:
	    /* FIXME: do something? */
	    break;

	case AppleWMNextWindow:
	    next_window (e->time, FALSE);
	    break;

	case AppleWMPreviousWindow:
	    next_window (e->time, TRUE);
	    break;
	}
	break;

    case AppleWMActivationNotify:
	if (e->kind >= 0 && e->kind < (int) (sizeof (activation_kinds)
					     / sizeof (activation_kinds[0])))
	{
	    DB("kind %s arg %d\n", activation_kinds[e->kind], e->arg);
	}
	else
	{
	    DB("kind %d arg %d\n", e->kind, e->arg);
	}

	switch (e->kind)
	{
	case AppleWMIsActive:
	    last_activation_time = e->time;
	    x_set_is_active (YES);

	    if (_proxy_pb)
		[x_selection_object () x_active:e->time];
	    break;

	case AppleWMIsInactive:
	    x_set_is_active (NO);
	    
	    if (_proxy_pb)
		[x_selection_object () x_inactive:e->time];
	    break;

    case AppleWMReloadPreferences:
        prefs_reload = YES;
        break;
	}
	break;

    case AppleWMPasteboardNotify:
	if (e->kind >= 0 && e->kind < (int) (sizeof (pasteboard_kinds)
					     / sizeof (pasteboard_kinds[0])))
	{
	    DB("kind %s arg %d\n", pasteboard_kinds[e->kind], e->arg);
	}
	else
	{
	    DB("kind %d arg %d\n", e->kind, e->arg);
	}

	switch (e->kind)
	{
	case AppleWMCopyToPasteboard:
	    [x_selection_object () x_copy:e->time];
	}
	break;
    }
}

static const char *
event_name (int type)
{
    switch (type)
    {
	static char buf[64];

	case KeyPress: return "KeyPress";
	case KeyRelease: return "KeyRelease";
	case ButtonPress: return "ButtonPress";
	case ButtonRelease: return "ButtonRelease";
	case MotionNotify: return "MotionNotify";
	case EnterNotify: return "EnterNotify";
	case LeaveNotify: return "LeaveNotify";
	case FocusIn: return "FocusIn";
	case FocusOut: return "FocusOut";
	case KeymapNotify: return "KeymapNotify";
	case Expose: return "Expose";
	case GraphicsExpose: return "GraphicsExpose";
	case NoExpose: return "NoExpose";
	case VisibilityNotify: return "VisibilityNotify";
	case CreateNotify: return "CreateNotify";
	case DestroyNotify: return "DestroyNotify";
	case UnmapNotify: return "UnmapNotify";
	case MapNotify: return "MapNotify";
	case MapRequest: return "MapRequest";
	case ReparentNotify: return "ReparentNotify";
	case ConfigureNotify: return "ConfigureNotify";
	case ConfigureRequest: return "ConfigureRequest";
	case GravityNotify: return "GravityNotify";
	case ResizeRequest: return "ResizeRequest";
	case CirculateNotify: return "CirculateNotify";
	case CirculateRequest: return "CirculateRequest";
	case PropertyNotify: return "PropertyNotify";
	case SelectionClear: return "SelectionClear";
	case SelectionRequest: return "SelectionRequest";
	case SelectionNotify: return "SelectionNotify";
	case ColormapNotify: return "ColormapNotify";
	case ClientMessage: return "ClientMessage";
	case MappingNotify: return "MappingNotify";

    default:
	if (type == x_shape_event_base + ShapeNotify)
	    return "ShapeNotify";
	else if (type == x_apple_wm_event_base + AppleWMControllerNotify)
	    return "AppleWMControllerNotify";
	sprintf (buf, "Unknown:%d", type);
	return buf;
    }
}

void
x_input_run (void)
{
    while (XPending (x_dpy) != 0)
    {
	XEvent e;

	XNextEvent (x_dpy, &e);

	DB("<%s window:%x>\n", event_name (e.type), e.xany.window);

	switch (e.type)
	{
	case KeyPress:
	case KeyRelease:
	    x_event_key (&e.xkey);
	    break;

	case ButtonPress:
	case ButtonRelease:
	    x_event_button (&e.xbutton);
	    break;

	case MotionNotify:
	    x_event_motion_notify (&e.xmotion);

	case FocusIn:
	case FocusOut:
	    x_event_focus (&e.xfocus);
	    break;

	case EnterNotify:
	case LeaveNotify:
	    x_event_crossing (&e.xcrossing);

	case DestroyNotify:
	    x_event_destroy_notify (&e.xdestroywindow);
	    break;

	case UnmapNotify:
	    x_event_unmap_notify (&e.xunmap);
	    break;

	case MapRequest:
	    x_event_map_request (&e.xmaprequest);
	    break;

	case ReparentNotify:
	    x_event_reparent_notify (&e.xreparent);
	    break;

	case ConfigureRequest:
	    x_event_configure_request (&e.xconfigurerequest);
	    break;

	case ConfigureNotify:
	    x_event_configure_notify (&e.xconfigure);
	    break;

	case PropertyNotify:
	    x_event_property_notify (&e.xproperty);
	    break;

	case ClientMessage:
	    x_event_client_message (&e.xclient);
	    break;

	case Expose:
	    x_event_expose (&e.xexpose);
	    break;

	case ColormapNotify:
	    x_event_colormap_notify (&e.xcolormap);
	    break;

	case SelectionClear:
	    [x_selection_object () clear_event:&e.xselectionclear];
	    break;

	case SelectionRequest:
	    [x_selection_object () request_event:&e.xselectionrequest];
	    break;

	case SelectionNotify:
	    [x_selection_object () notify_event:&e.xselection];
	    break;

	case MappingNotify:
	    x_event_mapping_notify (&e.xmapping);
	    break;

	default:
	    if (e.type == x_shape_event_base + ShapeNotify)
		x_event_shape_notify ((XShapeEvent *) &e);
	    else if (e.type - x_apple_wm_event_base >= 0
		     && e.type - x_apple_wm_event_base < AppleWMNumberEvents)
	    {
		x_event_apple_wm_notify ((XAppleWMNotifyEvent *) &e);
	    }
	    break;
	}

#ifdef CHECK_WINDOWS
	x_check_windows ();
#endif
    }
}

static int
add_input_socket (int sock, CFOptionFlags callback_types,
                  CFSocketCallBack callback, const CFSocketContext *ctx,
                  CFRunLoopSourceRef *cf_source)
{
    CFSocketRef cf_sock;
    
    cf_sock = CFSocketCreateWithNative (kCFAllocatorDefault, sock,
                                        callback_types, callback, ctx);
    if (cf_sock == NULL)
    {
        close (sock);
        return FALSE;
    }

    *cf_source = CFSocketCreateRunLoopSource (kCFAllocatorDefault,
                                              cf_sock, 0);
    CFRelease (cf_sock);
    
    if (*cf_source == NULL)
        return FALSE;

    CFRunLoopAddSource (CFRunLoopGetCurrent (),
                        *cf_source, kCFRunLoopDefaultMode);
    return TRUE;
}

static void
x_input_callback (CFSocketRef sock, CFSocketCallBackType type,
		  CFDataRef address, const void *data, void *info)
{
    x_input_run ();
}

void
x_input_register (void)
{
    if (!add_input_socket (ConnectionNumber (x_dpy), kCFSocketReadCallBack,
			   x_input_callback, NULL, &x_dpy_source))
    {
	exit (1);
    }
}
