/*
 *  notify.c -- Demonstrates how to bypass xv_main_loop() by creating your
 *  own event loop.
 */
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <xview/xview.h>
#include <xview/canvas.h>
#include <xview/textsw.h>
#include <xview/xv_xrect.h>

void    event_proc(), repaint_proc();
char    kbd_msg[128], ptr_msg[128], but_msg[128];
int	RUN = TRUE;
XFontStruct *font;
			/* Time out for select. */
#ifndef __linux__
struct timeval timeout = { 0, 250000 };
#else
struct timeval timeout; /* Linux changes timeval in select(), reinit every time */
#endif

main(argc, argv)
int argc;
char *argv[];
{
    Display	*dpy;
    Frame       frame;
    Textsw      textsw;
    Canvas      canvas;
    fd_set	readfds;
    int		fd;
    Notify_func my_destroy_func();

    /* Initialize XView */
    xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, NULL);

    /* Create windows -- base frame, canvas and a textsw. */
    frame = (Frame)xv_create(NULL, FRAME,
	XV_X,			10,
	XV_Y,			10,
	XV_LABEL,		"Notifier",
        NULL);

    /* Interpose on the frame's destroy proc so I know when the user has
     * selected quit from the menu.  I don't want to get into a position
     * where all my windows are gone, but my main loop is still spinning
     * away.
     */
    notify_interpose_destroy_func(frame, (Notify_func) my_destroy_func);

    canvas = xv_create(frame, CANVAS,
        XV_WIDTH,               520,
        XV_HEIGHT,              130,
        CANVAS_X_PAINT_WINDOW,  TRUE,
        CANVAS_REPAINT_PROC,    repaint_proc,
	/* Set attrs on the paint window */
	CANVAS_PAINTWINDOW_ATTRS,
            WIN_EVENT_PROC,     event_proc,
            WIN_CONSUME_EVENTS,
                KBD_DONE, KBD_USE, LOC_DRAG, LOC_MOVE, LOC_WINENTER,
                LOC_WINEXIT, WIN_ASCII_EVENTS, WIN_MOUSE_BUTTONS,
                NULL,
	    NULL,
        NULL);

    textsw = xv_create(frame, TEXTSW,
	XV_X, 		0,
	WIN_BELOW, 	canvas,
	XV_WIDTH,	520,
	XV_HEIGHT,	500,
	NULL);

    window_fit(frame);

    /* Initial messages */
    strcpy(kbd_msg, "Keyboard: key press events");
    strcpy(ptr_msg, "Pointer: pointer movement events");
    strcpy(but_msg, "Button: button press events");

    /* Get a nice big font */
    font = XLoadQueryFont(dpy = (Display *) XV_DISPLAY_FROM_WINDOW(frame), 
		"-b&h-lucida-medium-r-normal-sans-24-240-*-*-p-*-iso8859-1");

    /* Map the frame.  This is normally done for us by xv_main_loop(). */
    xv_set(frame, XV_SHOW, TRUE, 0);

    /* Get the server connection number. */
    fd = XConnectionNumber(dpy);

    /* Make sure everything gets over to the server. */
    XFlush(dpy);

    /* Create my own loop, only dispatching events when there is something
     * to dispatch.  When I'm not dispatching events, I'm free to do
     * as I please.
     */
    while (RUN) {
        FD_SET(fd, &readfds);

#ifdef __linux__
        timeout.tv_sec = 0;
        timeout.tv_usec = 250000;
#endif
	/* Check to see if the server has written to us. */
	if (select(FD_SETSIZE, &readfds, NULL, NULL, &timeout)) {

	    /* Read and dispatch events from the server. */
	    notify_dispatch();

	} else {
	    static int toggle = 0;

            /* Do my own thing here.  Just remember not to neglect incomming
	     * events.  (*Ping the notifier as often as possible*)
	     */

	    /* Erase contents of textsw. */
	    textsw_erase(textsw, 0, TEXTSW_INFINITY);

	    /* Load textsw with one of two files. */
	    xv_set(textsw,
	        TEXTSW_FILE_CONTENTS, toggle ? "/etc/fstab" : "/etc/passwd",
		TEXTSW_FIRST,	  0,
		NULL);

	    toggle = ~toggle;

	    /* If I've used any X routines, be sure to flush the requests
	     * through to the server.
	     */
	    XFlush(dpy);
	}
    }
    /* All done. */
}

/* This is called before the frame's destroy func (ie. I've interposed in
 * front of it.
 */
Notify_func
my_destroy_func(frame, status)
Frame 		frame;
Destroy_status  status;
{
    if (status == DESTROY_PROCESS_DEATH || status == DESTROY_CLEANUP)
	RUN = FALSE;

    /* Now call the frame's destroy func. */
    notify_next_destroy_func(frame, status);
}
	   

/*
 * event_proc()
 *      Called when an event is received in the canvas window.
 *      Updates the keyboard, pointer and button message strings
 *      and then calls repaint_proc() to paint them to the window.
 */
void
event_proc(window, event)
Xv_Window window;
Event    *event;
{
    if (event_is_ascii(event))
        sprintf(kbd_msg, "Keyboard: key '%c' %d pressed at %d,%d",
                event_action(event), event_action(event),
                event_x(event), event_y(event));
    else
        switch (event_action(event)) {
            case KBD_USE:
                sprintf(kbd_msg, "Keyboard: got keyboard focus");
                break;
            case KBD_DONE:
                sprintf(kbd_msg, "Keyboard: lost keyboard focus");
                break;
            case LOC_MOVE:
                sprintf(ptr_msg, "Pointer: moved to %d,%d",
                    event_x(event), event_y(event));
                break;
            case LOC_DRAG:
                sprintf(ptr_msg, "Pointer: dragged to %d,%d",
                    event_x(event), event_y(event));
                break;
            case LOC_WINENTER:
                sprintf(ptr_msg, "Pointer: entered window at %d,%d",
                    event_x(event), event_y(event));
                break;
            case LOC_WINEXIT:
                sprintf(ptr_msg, "Pointer: exited window at %d,%d",
                    event_x(event), event_y(event));
                break;
            case ACTION_SELECT:
            case MS_LEFT:
                sprintf(but_msg, "Button: Select (Left) at %d,%d",
                    event_x(event), event_y(event));
                break;
            case ACTION_ADJUST:
            case MS_MIDDLE:
                sprintf(but_msg, "Button: Adjust (Middle) at %d,%d",
                    event_x(event), event_y(event));
                break;
            case ACTION_MENU:
            case MS_RIGHT:
                sprintf(but_msg, "Button: Menu (Right) at %d,%d",
                    event_x(event), event_y(event));
                break;
            default:
                return;
        }

    /* call repaint proc directly to update messages */
    repaint_proc((Canvas)NULL, window,
        (Display *)xv_get(window, XV_DISPLAY),
        xv_get(window, XV_XID), (Xv_xrectlist *) NULL);
}

/*
 * repaint_proc()
 *      Called to repaint the canvas in response to damage events
 *      and the initial painting of the canvas window.
 *      Displays the keyboard, pointer and button message strings
 *      after erasing the previous messages.
 */
void
repaint_proc(canvas, paint_window, dpy, xwin, xrects)
Canvas        canvas;           /* Ignored */
Xv_Window     paint_window;     /* Ignored */
Display      *dpy;
Window        xwin;
Xv_xrectlist *xrects;           /* Ignored */
{
    GC gc = DefaultGC(dpy, DefaultScreen(dpy));
    XSetFont(dpy, gc, font->fid);

    XClearWindow(dpy, xwin);
    XDrawString(dpy, xwin, gc, 25, 35, kbd_msg, strlen(kbd_msg));
    XDrawString(dpy, xwin, gc, 25, 65, ptr_msg, strlen(ptr_msg));
    XDrawString(dpy, xwin, gc, 25, 95, but_msg, strlen(but_msg));
}
