/****************************************************************************
** $Id: qapplication_win.cpp,v 1.1 2002/06/24 14:12:11 thiagocorrea Exp $
**
** Implementation of X11 startup routines and event handling
**
** Created : 931029
**
** Copyright (C) 1992-2001 Trolltech AS.  All rights reserved.
**
** This file is part of the kernel module of the Qt GUI Toolkit.
**
** This file may be distributed under the terms of the Q Public License
** as defined by Trolltech AS of Norway and appearing in the file
** LICENSE.QPL included in the packaging of this file.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
** licenses for Unix/X11 may use this file in accordance with the Qt Commercial
** License Agreement provided with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
**   information about Qt Commercial License Agreements.
** See http://www.trolltech.com/qpl/ for QPL licensing information.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

// ### needed for solaris-g++ in beta5
#define QT_CLEAN_NAMESPACE

// Get the system specific includes and defines
#include "qplatformdefs.h"

// Solaris redefines connect to __xnet_connect when _XOPEN_SOURCE_EXTENDED is
// defined. This breaks our sources.
#if defined(connect)
# undef connect
#endif

#include "qapplication.h"
#include "qapplication_p.h"
//#include "qcolor_p.h"
//#include "qwidget.h"
//#include "qwidget_p.h"
#include "qobjectlist.h"
//#include "qwidgetlist.h"
//#include "qwidgetintdict.h"
#include "qbitarray.h"
//#include "qpainter.h"
//#include "qpixmapcache.h"
#include "qdatetime.h"
#include "qtextcodec.h"
#include "qdatastream.h"
#include "qbuffer.h"
//#include "qsocketnotifier.h"
//#include "qsessionmanager.h"
#include "qvaluelist.h"
#include "qdict.h"
#include "qguardedptr.h"
//#include "qclipboard.h"
//#include "qwhatsthis.h" // ######## dependency
#include "qsettings.h"
//#include "qstylefactory.h"
#include "qfileinfo.h"

// Input method stuff - UNFINISHED
//#include "qinputcontext_p.h"
//#include "qinternal_p.h" // shared double buffer cleanup

#if defined(QT_THREAD_SUPPORT)
# include "qthread.h"
#endif

#if defined(QT_DEBUG) && defined(Q_OS_LINUX)
# include "qfile.h"
#endif

#if defined(Q_OS_UNIX)
static int qt_thread_pipe[2];
#endif

#include "qt_windows.h"

#if defined(QT_MODULE_OPENGL)
#include <GL/glx.h>
#endif

#include <stdlib.h>
#include <ctype.h>
#include <locale.h>
#include <errno.h>
#include <time.h>

//#define X_NOT_BROKEN


/*****************************************************************************
  Internal variables and functions
 *****************************************************************************/

static const char *appName;			// application name
static const char *appFont	= 0;		// application font
static const char *appBGCol	= 0;		// application bg color
static const char *appFGCol	= 0;		// application fg color
static const char *appBTNCol	= 0;		// application btn color
static const char *mwGeometry	= 0;		// main widget geometry
static const char *mwTitle	= 0;		// main widget title
//Ming-Che 10/10
static char    *ximServer	= 0;		// XIM Server will connect to
static bool	mwIconic	= FALSE;	// main widget iconified
//Ming-Che 10/10
static bool	noxim		= FALSE;	// connect to xim or not
static char    *appDpyName	= 0;		// X11 display name
static bool	appForeignDpy	= FALSE;        // we didn't create display
static bool	appSync		= FALSE;	// X11 synchronization
#if defined(QT_DEBUG)
static bool	appNoGrab	= FALSE;	// X11 grabbing enabled
static bool	appDoGrab	= FALSE;	// X11 grabbing override (gdb)
#endif
static int	appScreen;			// X11 screen number
static int	screenCount;			// X11 screen count
static bool	app_save_rootinfo = FALSE;	// save root info

static bool	app_do_modal	= FALSE;	// modal mode
static int	app_Xfd;			// X network socket
static fd_set	app_readfds;			// fd set for reading
static fd_set	app_writefds;			// fd set for writing
static fd_set	app_exceptfds;			// fd set for exceptions

bool		qt_broken_wm		= FALSE;

// current focus model
static const int FocusModel_Unknown = -1;
static const int FocusModel_Other = 0;
static const int FocusModel_PointerRoot = 1;
static int qt_focus_model = -1;

// TRUE if Qt is compiled w/ XRender support and XRender exists on the connected
// Display
bool	qt_use_xrender	= FALSE;

// flags for extensions for special Languages, currently only for RTL languages
static bool 	qt_use_rtl_extensions = FALSE;

static int	mouseButtonPressed   = 0;	// last mouse button pressed
static int	mouseButtonState     = 0;	// mouse button state
static short	mouseXPos, mouseYPos;		// mouse pres position in act window
static short	mouseGlobalXPos, mouseGlobalYPos; // global mouse press position

static bool	    ignoreNextMouseReleaseEvent = FALSE; // ignore the next mouse release
							 // event if return from a modal
							 // widget

static bool	    popupCloseDownMode = FALSE;
static bool	    popupGrabOk;

static bool sm_blockUserInput = FALSE;		// session management

// one day in the future we will be able to have static objects in libraries....
static QGuardedPtr<QWidget>* activeBeforePopup = 0; // focus handling with popups

#if defined (QT_TABLET_SUPPORT)
// since XInput event classes aren't created until we actually open an XInput
// device, here is a static list that we will use later on...
const int INVALID_EVENT = -1;
const int TOTAL_XINPUT_EVENTS = 7;

XDevice *devStylus = NULL;
XDevice *devEraser = NULL;
XEventClass event_list_stylus[TOTAL_XINPUT_EVENTS];
XEventClass event_list_eraser[TOTAL_XINPUT_EVENTS];

int curr_events_stylus = 0;
int curr_events_eraser = 0;

// well, luckily we only need to do this once.
static int xinput_motion = INVALID_EVENT;
static int xinput_key_press = INVALID_EVENT;
static int xinput_key_release = INVALID_EVENT;
static int xinput_button_press = INVALID_EVENT;
static int xinput_button_release = INVALID_EVENT;

// making this assumption on XFree86, since we can only use 1 device,
// the pressure for the eraser and the stylus should be the same, if they aren't
// well, they certainly have a strange pen then...
static int max_pressure;
#endif

typedef void (*VFPTR)();
typedef QValueList<VFPTR> QVFuncList;
void qt_install_preselect_handler( VFPTR );
void qt_remove_preselect_handler( VFPTR );
static QVFuncList *qt_preselect_handler = 0;
void qt_install_postselect_handler( VFPTR );
void qt_remove_postselect_handler( VFPTR );
static QVFuncList *qt_postselect_handler = 0;
void qt_install_preselect_handler( VFPTR handler )
{
    if ( !qt_preselect_handler )
	qt_preselect_handler = new QVFuncList;
    qt_preselect_handler->append( handler );
}
void qt_remove_preselect_handler( VFPTR handler )
{
    if ( qt_preselect_handler ) {
	QVFuncList::Iterator it = qt_preselect_handler->find( handler );
	if ( it != qt_preselect_handler->end() )
		qt_preselect_handler->remove( it );
    }
}
void qt_install_postselect_handler( VFPTR handler )
{
    if ( !qt_postselect_handler )
	qt_postselect_handler = new QVFuncList;
    qt_postselect_handler->prepend( handler );
}
void qt_remove_postselect_handler( VFPTR handler )
{
    if ( qt_postselect_handler ) {
	QVFuncList::Iterator it = qt_postselect_handler->find( handler );
	if ( it != qt_postselect_handler->end() )
		qt_postselect_handler->remove( it );
    }
}



static void	initTimers();
static void	cleanupTimers();
static timeval	watchtime;			// watch if time is turned back
timeval		*qt_wait_timer();
timeval	*qt_wait_timer_max = 0;
int		qt_activate_timers();

#if !defined(QT_NO_XIM)
// static XIMStyle	xim_preferred_style	= XIMPreeditPosition | XIMStatusNothing;
#endif

static int composingKeycode=0;
static QTextCodec * input_mapper = 0;

//static void	qt_save_rootinfo();

int		qt_ncols_option  = 216;		// used in qcolor_x11.cpp
int		qt_visual_option = -1;
bool		qt_cmap_option	 = FALSE;

// stuff in qt_xdnd.cpp
// setup
extern void qt_xdnd_setup();
// x event handling
extern bool qt_xdnd_handle_badwindow();

extern void qt_x11_motifdnd_init();

// gui or non-gui from qapplication.cpp
extern bool qt_is_gui_used;

extern bool qt_resolve_symlinks; // from qapplication.cpp

// Paint event clipping magic
extern void qt_clear_paintevent_clipping();


// Palette handling
extern void qt_create_std_palette();


// ************************************************************************
// X Input Method support
// ************************************************************************
#define QT_NO_XIM
#if !defined(QT_NO_XIM)

#if defined(Q_C_CALLBACKS)
extern "C" {
#endif // Q_C_CALLBACKS

#ifdef USE_X11R6_XIM
    static void xim_create_callback(XIM /*im*/,
				    XPointer /*client_data*/,
				    XPointer /*call_data*/)
    {
	// qDebug("xim_create_callback");
	QApplication::create_xim();
    }

    static void xim_destroy_callback(XIM /*im*/,
				     XPointer /*client_data*/,
				     XPointer /*call_data*/)
    {
	// qDebug("xim_destroy_callback");
	QApplication::close_xim();
	XRegisterIMInstantiateCallback(appDpy, 0, 0, 0,
				       (XIMProc) xim_create_callback, 0);
    }

#endif // USE_X11R6_XIM

#if defined(Q_C_CALLBACKS)
}
#endif // Q_C_CALLBACKS

#endif // QT_NO_XIM



/*****************************************************************************
  Default X error handlers
 *****************************************************************************/

#if defined(Q_C_CALLBACKS)
extern "C" {
#endif

static bool x11_ignore_badwindow;
static bool x11_badwindow;

    // starts to ignore bad window errors from X
void qt_ignore_badwindow()
{
    x11_ignore_badwindow = TRUE;
    x11_badwindow = FALSE;
}

    // ends ignoring bad window errors and returns whether an error
    // had happen.
bool qt_badwindow()
{
    x11_ignore_badwindow = FALSE;
    return x11_badwindow;
}

#if defined(Q_C_CALLBACKS)
}
#endif


// Memory leak: if the app exits before qt_init_internal(), this dict
// isn't released correctly.
static bool create_atoms_now = 0;

/*****************************************************************************
  qt_x11_intern_atom() - efficiently interns an atom, now or later.

  If the application is being initialized, this function stores the
  adddress of the atom and qt_init_internal will do the actual work
  quickly. If the application is running, the atom is created here.

  Neither argument may point to temporary variables.
 *****************************************************************************/

static bool ignore_settings_change = FALSE;

static bool seems_like_KDE_is_running = FALSE;


/*
  Returns a truecolor visual (if there is one). 8-bit TrueColor visuals
  are ignored, unless the user has explicitly requested -visual TrueColor.
  The SGI X server usually has an 8 bit default visual, but the application
  can also ask for a truecolor visual. This is what we do if
  QApplication::colorSpec() is QApplication::ManyColor.
*/


#ifndef QT_NO_XRENDER
// ### REMOVE ME 3.1
// polute the stack to work around an XRender bug in XFree86 4.1
static void stack_ffffffff()
{
       long ffffffff[50];
       for(int i =0; i < 50; i++)
               ffffffff[i] = 0xffffffff;
	ffffffff[0] = ffffffff[0]; // avoid warnings
}
#endif // QT_NO_XRENDER

/*****************************************************************************
  qt_init() - initializes Qt for X11
 *****************************************************************************/

// ### This should be static but it isn't because of the friend declaration
// ### in qpaintdevice.h which then should have a static too but can't have
// ### it because "storage class specifiers invalid in friend function
// ### declarations" :-) Ideas anyone?
/*
void qt_init_internal( int *argcptr, char **argv,
		       Display *display, Qt::HANDLE visual, Qt::HANDLE colormap )
{
    if ( display ) {
	// Qt part of other application

	appForeignDpy = TRUE;
	appName = qstrdup( "Qt-subapplication" );
	appDpy  = display;
	app_Xfd = XConnectionNumber( appDpy );

	// Install default error handlers

	original_x_errhandler = XSetErrorHandler( qt_x_errhandler );
	original_xio_errhandler = XSetIOErrorHandler( qt_xio_errhandler );
    } else {
	// Qt controls everything (default)

	const char *p;
	int argc = *argcptr;
	int j;

	// Install default error handlers

	original_x_errhandler = XSetErrorHandler( qt_x_errhandler );
	original_xio_errhandler = XSetIOErrorHandler( qt_xio_errhandler );

	// Set application name

	p = strrchr( argv[0], '/' );
	appName = p ? p + 1 : argv[0];

	// Read global settings file
#if !defined(QT_NO_XIM)
	QString ximInputStyle = QSettings().readEntry( "/qt/XIMInputStyle", QObject::trUtf8( "On The Spot" ) );
	if ( ximInputStyle == "On The Spot" )
	    xim_preferred_style = XIMPreeditCallbacks | XIMStatusNothing;
	else if ( ximInputStyle == "Over The Spot" )
	    xim_preferred_style = XIMPreeditPosition | XIMStatusNothing;
	else if ( ximInputStyle == "Off The Spot" )
	    xim_preferred_style = XIMPreeditArea | XIMStatusArea;
	else if ( ximInputStyle == "Root" )
	    xim_preferred_style = XIMPreeditNothing | XIMStatusNothing;
#endif // !QT_NO_XIM

	// Get command line params

	j = 1;
	for ( int i=1; i<argc; i++ ) {
	    if ( argv[i] && *argv[i] != '-' ) {
		argv[j++] = argv[i];
		continue;
	    }
	    QCString arg = argv[i];
	    if ( arg == "-display" ) {
		if ( ++i < argc )
		    appDpyName = argv[i];
	    } else if ( arg == "-fn" || arg == "-font" ) {
		if ( ++i < argc )
		    appFont = argv[i];
	    } else if ( arg == "-bg" || arg == "-background" ) {
		if ( ++i < argc )
		    appBGCol = argv[i];
	    } else if ( arg == "-btn" || arg == "-button" ) {
		if ( ++i < argc )
		    appBTNCol = argv[i];
	    } else if ( arg == "-fg" || arg == "-foreground" ) {
		if ( ++i < argc )
		    appFGCol = argv[i];
	    } else if ( arg == "-name" ) {
		if ( ++i < argc )
		    appName = argv[i];
	    } else if ( arg == "-title" ) {
		if ( ++i < argc )
		    mwTitle = argv[i];
	    } else if ( arg == "-geometry" ) {
		if ( ++i < argc )
		    mwGeometry = argv[i];
		//Ming-Che 10/10
	    } else if ( arg == "-im" ) {
		if ( ++i < argc )
		    ximServer = argv[i];
	    } else if ( arg == "-noxim" ) {
		noxim=TRUE;
		//
	    } else if ( arg == "-iconic" ) {
		mwIconic = !mwIconic;
	    } else if ( arg == "-ncols" ) {   // xv and netscape use this name
		if ( ++i < argc )
		    qt_ncols_option = QMAX(0,atoi(argv[i]));
	    } else if ( arg == "-visual" ) {  // xv and netscape use this name
		if ( ++i < argc ) {
		    QCString s = QCString(argv[i]).lower();
		    if ( s == "truecolor" ) {
			qt_visual_option = TrueColor;
		    } else {
			// ### Should we honor any others?
		    }
		}
#ifndef QT_NO_XIM
	    } else if ( arg == "-inputstyle" ) {
		if ( ++i < argc ) {
		    QCString s = QCString(argv[i]).lower();
		    if ( s == "onthespot" )
			xim_preferred_style = XIMPreeditCallbacks |
					      XIMStatusNothing;
		    else if ( s == "overthespot" )
			xim_preferred_style = XIMPreeditPosition |
					      XIMStatusNothing;
		    else if ( s == "offthespot" )
			xim_preferred_style = XIMPreeditArea |
					      XIMStatusArea;
		    else if ( s == "root" )
			xim_preferred_style = XIMPreeditNothing |
					      XIMStatusNothing;
		}
#endif
	    } else if ( arg == "-cmap" ) {    // xv uses this name
		qt_cmap_option = TRUE;
	    }
#if defined(QT_DEBUG)
	    else if ( arg == "-sync" )
		appSync = !appSync;
	    else if ( arg == "-nograb" )
		appNoGrab = !appNoGrab;
	    else if ( arg == "-dograb" )
		appDoGrab = !appDoGrab;
#endif
	    else
		argv[j++] = argv[i];
	}

	*argcptr = j;

#if defined(QT_DEBUG) && defined(Q_OS_LINUX)
	if ( !appNoGrab && !appDoGrab ) {
	    QCString s;
	    s.sprintf( "/proc/%d/cmdline", getppid() );
	    QFile f( s );
	    if ( f.open( IO_ReadOnly ) ) {
		s.truncate( 0 );
		int c;
		while ( (c = f.getch()) > 0 ) {
		    if ( c == '/' )
			s.truncate( 0 );
		    else
			s += (char)c;
		}
		if ( s == "gdb" ) {
		    appNoGrab = TRUE;
		    qDebug( "Qt: gdb: -nograb added to command-line options.\n"
			    "\t Use the -dograb option to enforce grabbing." );
		}
		f.close();
	    }
	}
#endif
	// Connect to X server

	if( qt_is_gui_used ) {
	    if ( ( appDpy = XOpenDisplay(appDpyName) ) == 0 ) {
		qWarning( "%s: cannot connect to X server %s", appName,
			  XDisplayName(appDpyName) );
		exit( 1 );
	    }
	    app_Xfd = XConnectionNumber( appDpy );	// set X network socket

	    if ( appSync )				// if "-sync" argument
		XSynchronize( appDpy, TRUE );
	}
    }
    // Common code, regardless of whether display is foreign.

    // Get X parameters

    if( qt_is_gui_used ) {
	appScreen = DefaultScreen(appDpy);
	screenCount = ScreenCount(appDpy);
	appRootWin = RootWindow(appDpy,appScreen);

	// Set X paintdevice parameters

	QPaintDevice::x_appdisplay = appDpy;
	QPaintDevice::x_appscreen = appScreen;
	QPaintDevice::x_appdepth = DefaultDepth(appDpy,appScreen);
	QPaintDevice::x_appcells = DisplayCells(appDpy,appScreen);

	// work around a bug in vnc where DisplayCells returns 8 when Xvnc is run
	// with depth 8
	if (QPaintDevice::x_appdepth == 8)
	    QPaintDevice::x_appcells = 256;

	Visual *vis;
	if (! visual) {
	    // use the default visual
	    vis = DefaultVisual(appDpy,appScreen);
	    QPaintDevice::x_appdefvisual = TRUE;

	    if ( qt_visual_option == TrueColor ||
		 QApplication::colorSpec() == QApplication::ManyColor ) {
		// find custom visual
		vis = find_truecolor_visual( appDpy, &QPaintDevice::x_appdepth,
					     &QPaintDevice::x_appcells );

		QPaintDevice::x_appdefvisual =
		    (XVisualIDFromVisual(vis) ==
		     XVisualIDFromVisual(DefaultVisual(appDpy,appScreen)));
		QPaintDevice::x_appvisual = vis;
	    }

#if defined( QT_MODULE_OPENGL )
	    // If we are using OpenGL widgets we HAVE to make sure
	    // that the default visual is GL enabled, otherwise it
	    // will wreck havock when e.g trying to render to
	    // GLXPixmaps via QPixmap. This is because QPixmap is
	    // always created with a QPaintDevice that uses
	    // x_appvisual per default. Preferably, use a visual that
            // has depth and stencil buffers.

	    int nvis;
	    XVisualInfo * vi;
	    XVisualInfo visInfo;
	    memset( &visInfo, 0, sizeof(XVisualInfo) );
	    visInfo.visualid = XVisualIDFromVisual( vis );
	    visInfo.screen = appScreen;
	    vi = XGetVisualInfo( appDpy, VisualIDMask | VisualScreenMask,
				 &visInfo, &nvis );
	    if ( vi ) {
		int useGL;
		glXGetConfig( appDpy, vi, GLX_USE_GL, &useGL );
		if ( !useGL ) {
		    // We have to find another visual that is GL capable
		    int i;
		    XVisualInfo * visuals;
		    memset( &visInfo, 0, sizeof(XVisualInfo) );
		    visInfo.screen = appScreen;
		    visInfo.c_class = vi->c_class;
		    visInfo.depth = vi->depth;
		    visuals = XGetVisualInfo( appDpy, VisualClassMask |
					      VisualDepthMask |
					      VisualScreenMask, &visInfo,
					      &nvis );
		    if ( visuals ) {
			for ( i = 0; i < nvis; i++ ) {
			    glXGetConfig( appDpy, &visuals[i], GLX_USE_GL,
					  &useGL );
			    if ( useGL ) {
				vis = visuals[i].visual;
				QPaintDevice::x_appdefvisual = FALSE;
				break;
			    }
			}
			XFree( visuals );
		    }
		}
		XFree( vi );
	    }
#endif
	    QPaintDevice::x_appvisual = vis;
	} else {
	    // use the provided visual
	    vis = (Visual *) visual;
	    QPaintDevice::x_appvisual = vis;
	    QPaintDevice::x_appdefvisual = FALSE;
	}

	if (! colormap) {
	    if ( vis->c_class == TrueColor )
		QPaintDevice::x_appdefcolormap = QPaintDevice::x_appdefvisual;
	    else
		QPaintDevice::x_appdefcolormap =
		    !qt_cmap_option && QPaintDevice::x_appdefvisual;

	    if ( QPaintDevice::x_appdefcolormap ) {
		XStandardColormap *stdcmap;
		VisualID vid = XVisualIDFromVisual((Visual *) QPaintDevice::x_appvisual);
		int i, count;

		QPaintDevice::x_appcolormap = 0;

		QString serverVendor( ServerVendor( appDpy) );
                if ( ! serverVendor.contains( "Hewlett-Packard" ) ) {
		    // on HPUX 10.20 local displays, the RGB_DEFAULT_MAP colormap
		    // doesn't give us correct colors. Why this happens, I have
		    // no clue, so we disable this for HPUX
		    if (XGetRGBColormaps(appDpy, appRootWin,
				     &stdcmap, &count, XA_RGB_DEFAULT_MAP)) {
		        i = 0;
		        while (i < count && QPaintDevice::x_appcolormap == 0) {
			    if (stdcmap[i].visualid == vid)
			        QPaintDevice::x_appcolormap = stdcmap[i].colormap;
			    i++;
		        }

		        XFree( (char *)stdcmap );
		    }
		}

		if (QPaintDevice::x_appcolormap == 0)
		    QPaintDevice::x_appcolormap = DefaultColormap(appDpy,appScreen);
	    } else
		QPaintDevice::x_appcolormap = XCreateColormap(appDpy, appRootWin,
							      vis, AllocNone);
	} else {
	    QPaintDevice::x_appcolormap = colormap;
	    QPaintDevice::x_appdefcolormap = FALSE;
	}

	// Support protocols

	qt_x11_intern_atom( "WM_PROTOCOLS", &qt_wm_protocols );
	qt_x11_intern_atom( "WM_DELETE_WINDOW", &qt_wm_delete_window );
	qt_x11_intern_atom( "WM_STATE", &qt_wm_state );
	qt_x11_intern_atom( "WM_TAKE_FOCUS", &qt_wm_take_focus );
	qt_x11_intern_atom( "WM_CLIENT_LEADER", &qt_wm_client_leader);
	qt_x11_intern_atom( "WINDOW_ROLE", &qt_window_role);
	qt_x11_intern_atom( "SM_CLIENT_ID", &qt_sm_client_id);
	qt_x11_intern_atom( "CLIPBOARD", &qt_xa_clipboard );
	qt_x11_intern_atom( "RESOURCE_MANAGER", &qt_resource_manager );
	qt_x11_intern_atom( "INCR", &qt_x_incr );
	qt_x11_intern_atom( "_XSETROOT_ID", &qt_xsetroot_id );
	qt_x11_intern_atom( "_QT_SELECTION", &qt_selection_property );
	qt_x11_intern_atom( "_QT_CLIPBOARD_SENTINEL", &qt_clipboard_sentinel );
	qt_x11_intern_atom( "_QT_SELECTION_SENTINEL", &qt_selection_sentinel );
	qt_x11_intern_atom( "_QT_SCROLL_DONE", &qt_qt_scrolldone );
	qt_x11_intern_atom( "_QT_DESKTOP_PROPERTIES", &qt_desktop_properties );
	qt_x11_intern_atom( "_QT_INPUT_ENCODING", &qt_input_encoding );
	qt_x11_intern_atom( "_QT_SIZEGRIP", &qt_sizegrip );
	qt_x11_intern_atom( "_NET_WM_CONTEXT_HELP", &qt_net_wm_context_help );
	qt_x11_intern_atom( "_MOTIF_WM_HINTS", &qt_xa_motif_wm_hints );
	qt_x11_intern_atom( "KWIN_RUNNING", &qt_kwin_running );
	qt_x11_intern_atom( "KWM_RUNNING", &qt_kwm_running );
	qt_x11_intern_atom( "GNOME_BACKGROUND_PROPERTIES", &qt_gbackground_properties );

	QString atomname("_QT_SETTINGS_TIMESTAMP_");
	atomname += XDisplayName(appDpyName);
	qt_x11_intern_atom( atomname.latin1(), &qt_settings_timestamp );

	qt_x11_intern_atom( "_NET_SUPPORTED", &qt_net_supported );
	qt_x11_intern_atom( "_NET_VIRTUAL_ROOTS", &qt_net_virtual_roots );
	qt_x11_intern_atom( "_NET_WM_STATE", &qt_net_wm_state );
	qt_x11_intern_atom( "_NET_WM_STATE_MODAL", &qt_net_wm_state_modal );
	qt_x11_intern_atom( "_NET_WM_STATE_MAXIMIZED_VERT", &qt_net_wm_state_max_v );
	qt_x11_intern_atom( "_NET_WM_STATE_MAXIMIZED_HORZ", &qt_net_wm_state_max_h );
	qt_x11_intern_atom( "_NET_WM_WINDOW_TYPE", &qt_net_wm_window_type );
	qt_x11_intern_atom( "_NET_WM_WINDOW_TYPE_NORMAL",
			    &qt_net_wm_window_type_normal );
	qt_x11_intern_atom( "_NET_WM_WINDOW_TYPE_DIALOG",
			    &qt_net_wm_window_type_dialog );
	qt_x11_intern_atom( "_NET_WM_WINDOW_TYPE_TOOLBAR",
			    &qt_net_wm_window_type_toolbar );
	qt_x11_intern_atom( "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE",
			    &qt_net_wm_window_type_override );
	qt_x11_intern_atom( "_KDE_NET_WM_FRAME_STRUT", &qt_net_wm_frame_strut );
	qt_x11_intern_atom( "_NET_WM_STATE_STAYS_ON_TOP",
			    &qt_net_wm_state_stays_on_top );
	qt_x11_intern_atom( "ENLIGHTENMENT_DESKTOP", &qt_enlightenment_desktop );

	qt_xdnd_setup();
	qt_x11_motifdnd_init();

	// Finally create all atoms
	qt_x11_process_intern_atoms();

	// initialize NET lists
	qt_get_net_supported();
	qt_get_net_virtual_roots();

#ifndef QT_NO_XRENDER
	// See if XRender is supported on the connected display
	int xrender_eventbase, xrender_errorbase;
	if (XRenderQueryExtension(appDpy, &xrender_eventbase, &xrender_errorbase)) {
	    // XRender is supported, let's see if we have a PictFormat for the
	    // default visual
            stack_ffffffff();
	    XRenderPictFormat *format =
		XRenderFindVisualFormat(appDpy,
					(Visual *) QPaintDevice::x_appvisual);
	    qt_use_xrender = (format != 0) && (QPaintDevice::x_appdepth != 8);
	}
#endif // QT_NO_XRENDER

#ifndef QT_NO_XKB
	// If XKB is detected, set the GrabsUseXKBState option so input method
	// compositions continue to work (ie. deadkeys)
	unsigned int state = XkbPCF_GrabsUseXKBStateMask;
	(void) XkbSetPerClientControls(appDpy, state, &state);
#endif

	// Misc. initialization

	QColor::initialize();
	QFont::initialize();
	QCursor::initialize();
	QPainter::initialize();
    }

#if defined(QT_THREAD_SUPPORT)
    QThread::initialize();
#endif

	gettimeofday( &watchtime, 0 );

    if( qt_is_gui_used ) {
	qApp->setName( appName );

	XSelectInput( appDpy, appRootWin,
		      KeymapStateMask |
		      EnterWindowMask | LeaveWindowMask |
		      PropertyChangeMask
		      );
    }

    // XIM segfaults on Solaris with "C" locale!
    // The idea was that the "en_US" locale is maybe installed on all systems
    // with a "C" locale and could be used as a fallback instead of "C". This
    // is not the case.
    // We'll have to take a XIM / no XIM decision at run-time.
    // Taking a decision at compile-time using QT_NO_XIM is not enough.
#if defined (Q_OS_SOLARIS) && !defined(QT_NO_XIM)
    const char* locale = ::setlocale( LC_ALL, "" );
    if ( !locale || qstrcmp( locale, "C" ) == 0 ) {
	locale = ::setlocale( LC_ALL, "en_US" );
	Q_ASSERT( qstrcmp( locale, "en_US" ) == 0 );
    }
#else
    setlocale( LC_ALL, "" );		// use correct char set mapping
#endif

    setlocale( LC_NUMERIC, "C" );	// make sprintf()/scanf() work

    if ( qt_is_gui_used ) {

#ifndef QT_NO_XIM
	qt_xim = 0;
	QString ximServerName(ximServer);
	if (ximServer)
	    ximServerName.prepend("@im=");
	else
	    ximServerName = "";

	if ( !XSupportsLocale() )
	    qWarning("Qt: Locales not supported on X server");

#ifdef USE_X11R6_XIM
	else if ( XSetLocaleModifiers (ximServerName.ascii()) == 0 )
	    qWarning( "Qt: Cannot set locale modifiers: %s",
		      ximServerName.ascii());
	else if (! noxim)
	    XRegisterIMInstantiateCallback(appDpy, 0, 0, 0,
					   (XIMProc) xim_create_callback, 0);
#else // !USE_X11R6_XIM
	else if ( XSetLocaleModifiers ("") == 0 )
	    qWarning("Qt: Cannot set locale modifiers");
	else if (! noxim)
	    QApplication::create_xim();
#endif // USE_X11R6_XIM
#endif // QT_NO_XIM

	qt_set_input_encoding();

	// be smart about the size of the default font. most X servers have helvetica
	// 12 point available at 2 resolutions:
	//     75dpi (12 pixels) and 100dpi (17 pixels).
	// At 95 DPI, a 12 point font should be 16 pixels tall - in which case a 17
	// pixel font is a closer match than a 12 pixel font
	int ptsz =
	    (int) ( ( ( QPaintDevice::x11AppDpiY() >= 95 ? 17. : 12. ) *
		      72. / (float) QPaintDevice::x11AppDpiY() ) + 0.5 );

	QFont f(
#ifdef Q_OS_SOLARIS
		"Interface System",
#else
		"Helvetica",
#endif // Q_OS_SOLARIS
		ptsz );
	QApplication::setFont( f );

	qt_set_x11_resources( appFont, appFGCol, appBGCol, appBTNCol);

#if defined (QT_TABLET_SUPPORT)
	int ndev,
	    i,
	    j;
	bool gotStylus,
	     gotEraser;
	XDeviceInfo *devices,
	    *devs;
	XInputClassInfo *ip;
	XAnyClassPtr any;
	XValuatorInfoPtr v;
	XAxisInfoPtr a;
	XDevice *dev;
	XEventClass *ev_class;
	int curr_event_count;

#if !defined(Q_OS_IRIX)
	// XFree86 divides a stylus and eraser into 2 devices, so we must do for both...
	const char XFREENAMESTYLUS[] = "stylus";
	const char XFREENAMEERASER[] = "eraser";
#endif

	devices = XListInputDevices( appDpy, &ndev);
	if ( devices == NULL ) {
	    qWarning( "Failed to get list of devices" );
	    ndev = -1;
	}
	dev = NULL;
	for ( devs = devices, i = 0; i < ndev; i++, devs++ ) {
	    gotStylus = gotEraser = FALSE;
#if defined(Q_OS_IRIX)
	    if ( !strncmp( devs->name, WACOM_NAME, sizeof(WACOM_NAME) - 1 ) ) {
		gotStylus = TRUE;
#else
	    if ( !strncmp( devs->name, XFREENAMESTYLUS, sizeof(XFREENAMESTYLUS) - 1 ) )
		gotStylus = TRUE;
	    else if ( !strncmp( devs->name, XFREENAMEERASER, sizeof(XFREENAMEERASER) - 1 ) )
		gotEraser = TRUE;
	    if ( gotStylus || gotEraser ) {
#endif
		// I only wanted to do this once, so wrap pointers around these
		curr_event_count = 0;

		if ( gotStylus ) {
		    devStylus = XOpenDevice( appDpy, devs->id );
		    dev = devStylus;
		    ev_class = event_list_stylus;
		} else if ( gotEraser ) {
		    devEraser = XOpenDevice( appDpy, devs->id );
		    dev = devEraser;
		    ev_class = event_list_eraser;
		}
		if ( dev == NULL ) {
		    qWarning( "Failed to open device" );
		} else {
		    if ( dev->num_classes > 0 ) {
			for ( ip = dev->classes, j = 0; j < devs->num_classes;
			      ip++, j++ ) {
			    switch ( ip->input_class ) {
			    case KeyClass:
				DeviceKeyPress( dev, xinput_key_press,
						ev_class[curr_event_count] );
				curr_event_count++;
				DeviceKeyRelease( dev, xinput_key_release,
						  ev_class[curr_event_count] );
				curr_event_count++;
				break;
			    case ButtonClass:
				DeviceButtonPress( dev, xinput_button_press,
						   ev_class[curr_event_count] );
				curr_event_count++;
				DeviceButtonRelease( dev, xinput_button_release,
						     ev_class[curr_event_count] );
				curr_event_count++;
				break;
			    case ValuatorClass:
				// I'm only going to be interested in motion when the
				// stylus is already down anyway!
				DeviceMotionNotify( dev, xinput_motion,
						    ev_class[curr_event_count] );
				curr_event_count++;
				break;
			    default:
				break;
			    }
			}
		    }
		}
		// get the min/max value for pressure!
		any = (XAnyClassPtr) ( devs->inputclassinfo );
		if ( dev == devStylus ) {
		    curr_events_stylus = curr_event_count;
		    for (j = 0; j < devs->num_classes; j++) {
			if ( any->c_class == ValuatorClass ) {
			    v = (XValuatorInfoPtr) any;
			    a = (XAxisInfoPtr) ((char *) v +
						sizeof (XValuatorInfo));
#if defined (Q_OS_IRIX)
			    max_pressure = a[WAC_PRESSURE_I].max_value;
#else
			    max_pressure = a[2].max_value;
#endif
			    // got the max pressure no need to go further...
			    break;
			}
			any = (XAnyClassPtr) ((char *) any + any->length);
		    }
		} else {
		    curr_events_eraser = curr_event_count;
		}
		// at this point we are assuming there is only one
		// wacom device...
#if defined (Q_OS_IRIX)
		if ( devStylus != NULL ) {
#else
		if ( devStylus != NULL && devEraser != NULL ) {
#endif
		    break;
		}
	    }
	} // end for loop
	XFreeDeviceList( devices );
#endif // QT_TABLET_SUPPORT

    } else {
	// read some non-GUI settings when not using the X server...

	QSettings settings;

	// read library (ie. plugin) path list
	QStringList pathlist =
	    settings.readListEntry("/qt/libraryPath", ':');
	if (! pathlist.isEmpty()) {
	    QStringList::ConstIterator it = pathlist.begin();
	    while (it != pathlist.end())
		QApplication::addLibraryPath(*it++);
	}

	QString defaultcodec = settings.readEntry("/qt/defaultCodec", "none");
	if (defaultcodec != "none") {
	    QTextCodec *codec = QTextCodec::codecForName(defaultcodec);
	    if (codec)
		qApp->setDefaultCodec(codec);
	}

	qt_resolve_symlinks =
	    settings.readBoolEntry("/qt/resolveSymlinks", TRUE);
    }

#if defined(Q_OS_UNIX)
    pipe( qt_thread_pipe );
#endif

}
*/

#ifndef QT_NO_STYLE
 // run-time search for default style
void QApplication::x11_initialize_style()
{
    Atom type;
    int format;
    unsigned long length, after;
    uchar *data;
    if ( app_style )
	return;
    if ( !seems_like_KDE_is_running ) {
	if ( XGetWindowProperty( appDpy, appRootWin, qt_kwin_running, 0, 1,
				 False, AnyPropertyType, &type, &format,
				 &length, &after, &data ) == Success
	     && length ) {
	    seems_like_KDE_is_running = TRUE;
	    if ( data )
		XFree( (char *)data );
	}
    }
    if ( !seems_like_KDE_is_running ) {
	if ( XGetWindowProperty( appDpy, appRootWin, qt_kwm_running, 0, 1,
				 False, AnyPropertyType, &type, &format,
				 &length, &after, &data ) == Success
	     && length ) {
	    seems_like_KDE_is_running = TRUE; // KDE1, to be precise
	    if ( data )
		XFree( (char *)data );
	}
    }
    if ( seems_like_KDE_is_running ) {
#ifndef QT_NO_STYLE_WINDOWS
	// check if KDE's styles are available
	app_style = QStyleFactory::create("highcolor");
	if(!app_style) 	// No, use windows style on KDE
	    app_style = QStyleFactory::create("windows");
#endif
    } else { // maybe another desktop?
	if ( XGetWindowProperty( appDpy, appRootWin, qt_gbackground_properties, 0, 1,
				 False, AnyPropertyType, &type, &format,
				 &length, &after, &data ) == Success
	     && length ) {
#if 0
#ifndef QT_NO_STYLE_MOTIFPLUS
	    // default to MotifPlus with hovering
	    app_style = new QMotifPlusStyle( TRUE );
#endif
#endif
	    if ( data )
		XFree( (char *)data );
	}
    }
}
#endif

void qt_init( int *argcptr, char **argv, QApplication::Type )
{
//    qt_init_internal( argcptr, argv, 0, 0, 0 );
}

/*****************************************************************************
  qt_cleanup() - cleans up when the application is finished
 *****************************************************************************/

void qt_cleanup()
{
    if ( app_save_rootinfo )			// root window must keep state
    cleanupTimers();
    if ( qt_is_gui_used ) {
    }
#if defined(QT_THREAD_SUPPORT)
    QThread::cleanup();
#endif

#if defined (QT_TABLET_SUPPORT)
    if ( devStylus != NULL )
	XCloseDevice( appDpy, devStylus );
    if ( devEraser != NULL )
	XCloseDevice( appDpy, devEraser );
#endif

#if !defined(QT_NO_XIM)
    if ( qt_xim )
	QApplication::close_xim();
#endif

    if ( appForeignDpy ) {
	delete [] (char *)appName;
	appName = 0;
    }

    delete activeBeforePopup;
    activeBeforePopup = 0;

#ifdef Q_OS_UNIX
    close(qt_thread_pipe[0]);
    close(qt_thread_pipe[1]);
#endif // Q_OS_UNIX
}


/*****************************************************************************
  Platform specific global and internal functions
 *****************************************************************************/

const char *qAppName()				// get application name
{
    return appName;
}

int qt_xscreen()				// get current X screen
{
    return appScreen;
}

bool qt_nograb()				// application no-grab option
{
#if defined(QT_DEBUG)
    return appNoGrab;
#else
    return FALSE;
#endif
}


/*****************************************************************************
  Platform specific QApplication members
 *****************************************************************************/

/*!
    \fn QWidget *QApplication::mainWidget() const

    Returns the main application widget, or 0 if there is no main
    widget.

    \sa setMainWidget()
*/

/*!
    Sets the application's main widget to \a mainWidget.

    In most respects the main widget is like any other widget, except
    that if it is deleted, the application exits.

    You need not have a main widget; connecting lastWindowClosed() to
    quit() is an alternative.

    For X11, this function also resizes and moves the main widget
    according to the \e -geometry command-line option, so you should
    set the default geometry (using \l QWidget::setGeometry()) before
    calling setMainWidget().

    \sa mainWidget(), exec(), quit()
*/


#ifndef QT_NO_CURSOR

/*****************************************************************************
  QApplication cursor stack
 *****************************************************************************/

typedef QPtrList<QCursor> QCursorList;

static QCursorList *cursorStack = 0;

/*!
    \fn QCursor *QApplication::overrideCursor()

    Returns the active application override cursor.

    This function returns 0 if no application cursor has been defined
    (i.e. the internal cursor stack is empty).

    \sa setOverrideCursor(), restoreOverrideCursor()
*/

/*!
    Sets the application override cursor to \a cursor.

    Application override cursors are intended for showing the user
    that the application is in a special state, for example during an
    operation that might take some time.

    This cursor will be displayed in all the application's widgets
    until restoreOverrideCursor() or another setOverrideCursor() is
    called.

    Application cursors are stored on an internal stack.
    setOverrideCursor() pushes the cursor onto the stack, and
    restoreOverrideCursor() pops the active cursor off the stack.
    Every setOverrideCursor() must eventually be followed by a
    corresponding restoreOverrideCursor(), otherwise the stack will
    never be emptied.

    If \a replace is TRUE, the new cursor will replace the last
    override cursor (the stack keeps its depth). If \a replace is
    FALSE, the new stack is pushed onto the top of the stack.

    Example:
    \code
	QApplication::setOverrideCursor( Qt::WaitCursor );
	calculateHugeMandelbrot();              // lunch time...
	QApplication::restoreOverrideCursor();
    \endcode

    \sa overrideCursor(), restoreOverrideCursor(), QWidget::setCursor()
*/

void QApplication::setOverrideCursor( const QCursor &cursor, bool replace )
{
    if ( !cursorStack ) {
	cursorStack = new QCursorList;
	Q_CHECK_PTR( cursorStack );
	cursorStack->setAutoDelete( TRUE );
    }
    app_cursor = new QCursor( cursor );
    Q_CHECK_PTR( app_cursor );
    if ( replace )
	cursorStack->removeLast();
    cursorStack->append( app_cursor );

    QWidget* amw = activeModalWidget();
    QWidgetIntDictIt it( *((QWidgetIntDict*)QWidget::mapper) );
    register QWidget *w;
    while ( (w=it.current()) ) {		// for all widgets that have
	if ( w->testWState(WState_OwnCursor) &&
	     ( !amw || !w->isVisible() || w->topLevelWidget() == amw ) )	//   set a cursor
	    XDefineCursor( w->x11Display(), w->winId(), app_cursor->handle() );
	++it;
    }
    XFlush( appDpy );				// make X execute it NOW
}

/*!
    Undoes the last setOverrideCursor().

    If setOverrideCursor() has been called twice, calling
    restoreOverrideCursor() will activate the first cursor set.
    Calling this function a second time restores the original widgets'
    cursors.

    \sa setOverrideCursor(), overrideCursor().
*/

void QApplication::restoreOverrideCursor()
{
    if ( !cursorStack )				// no cursor stack
	return;
    cursorStack->removeLast();
    app_cursor = cursorStack->last();
    if ( QWidget::mapper != 0 && !closingDown() ) {
	QWidgetIntDictIt it( *((QWidgetIntDict*)QWidget::mapper) );
	register QWidget *w;
	while ( (w=it.current()) ) {		// set back to original cursors
	    if ( w->testWState(WState_OwnCursor) )
		XDefineCursor( w->x11Display(), w->winId(),
			       app_cursor ? app_cursor->handle()
			       : w->cursor().handle() );
	    ++it;
	}
	XFlush( appDpy );
    }
    if ( !app_cursor ) {
	delete cursorStack;
	cursorStack = 0;
    }
}

#endif

/*!
    \fn bool QApplication::hasGlobalMouseTracking()

    Returns TRUE if global mouse tracking is enabled; otherwise
    returns FALSE.

    \sa setGlobalMouseTracking()
*/

/*!
    Enables global mouse tracking if \a enable is TRUE, or disables it
    if \a enable is FALSE.

    Enabling global mouse tracking makes it possible for widget event
    filters or application event filters to get all mouse move events,
    even when no button is depressed. This is useful for special GUI
    elements, e.g. tooltips.

    Global mouse tracking does not affect widgets and their
    mouseMoveEvent(). For a widget to get mouse move events when no
    button is depressed, it must do QWidget::setMouseTracking(TRUE).

    This function uses an internal counter. Each
    setGlobalMouseTracking(TRUE) must have a corresponding
    setGlobalMouseTracking(FALSE):
    \code
	// at this point global mouse tracking is off
	QApplication::setGlobalMouseTracking( TRUE );
	QApplication::setGlobalMouseTracking( TRUE );
	QApplication::setGlobalMouseTracking( FALSE );
	// at this point it's still on
	QApplication::setGlobalMouseTracking( FALSE );
	// but now it's off
    \endcode

    \sa hasGlobalMouseTracking(), QWidget::hasMouseTracking()
*/

void QApplication::setGlobalMouseTracking( bool enable )
{
}


/*****************************************************************************
  Routines to find a Qt widget from a screen position
 *****************************************************************************/


/*!
    Returns a pointer to the widget at global screen position \a
    (x, y), or 0 if there is no Qt widget there.

    If \a child is FALSE and there is a child widget at position \a
    (x, y), the top-level widget containing it is returned. If \a child
    is TRUE the child widget at position \a (x, y) is returned.

    This function is normally rather slow.

    \sa QCursor::pos(), QWidget::grabMouse(), QWidget::grabKeyboard()
*/

/*!
    \overload QWidget *QApplication::widgetAt( const QPoint &pos, bool child )

    Returns a pointer to the widget at global screen position \a pos,
    or 0 if there is no Qt widget there.

    If \a child is FALSE and there is a child widget at position \a
    pos, the top-level widget containing it is returned. If \a child
    is TRUE the child widget at position \a pos is returned.
*/


/*!
    Flushes the X event queue in the X11 implementation. This normally
    returns almost immediately. Does nothing on other platforms.

    \sa syncX()
*/

/*!
    Flushes the window system specific event queues.

    If you are doing graphical changes inside a loop that does not
    return to the event loop on asynchronous window systems like X11
    or double buffered window systems like MacOS X, and you want to
    visualize these changes immediately (e.g. Splash Screens), call
    this function.

    \sa flushX() sendPostedEvents() QPainter::flush()
*/

void QApplication::flush()
{
//    flushX();
}



/*!
    Sounds the bell, using the default volume and sound.
*/

void QApplication::beep()
{
}



/*****************************************************************************
  Special lookup functions for windows that have been reparented recently
 *****************************************************************************/


/*****************************************************************************
  Socket notifier (type: 0=read, 1=write, 2=exception)

  The QSocketNotifier class (qsocketnotifier.h) provides installable callbacks
  for select() through the internal function qt_set_socket_handler().
 *****************************************************************************/

struct QSockNot {
    QObject *obj;
    int	     fd;
    fd_set  *queue;
};

typedef QPtrList<QSockNot> QSNList;
typedef QPtrListIterator<QSockNot> QSNListIt;

static int	sn_highest = -1;
static QSNList *sn_read	   = 0;
static QSNList *sn_write   = 0;
static QSNList *sn_except  = 0;

static fd_set	sn_readfds;			// fd set for reading
static fd_set	sn_writefds;			// fd set for writing
static fd_set	sn_exceptfds;			// fd set for exceptions
static fd_set	sn_queued_read;
static fd_set	sn_queued_write;
static fd_set	sn_queued_except;

static struct SN_Type {
    QSNList **list;
    fd_set   *fdspec;
    fd_set   *fdres;
    fd_set   *queue;
} sn_vec[3] = {
    { &sn_read,	  &sn_readfds,	 &app_readfds,   &sn_queued_read },
    { &sn_write,  &sn_writefds,	 &app_writefds,  &sn_queued_write },
    { &sn_except, &sn_exceptfds, &app_exceptfds, &sn_queued_except } };


static QSNList *sn_act_list = 0;


static void sn_cleanup()
{
    delete sn_act_list;
    sn_act_list = 0;
    for ( int i=0; i<3; i++ ) {
	delete *sn_vec[i].list;
	*sn_vec[i].list = 0;
    }
}


static void sn_init()
{
    if ( !sn_act_list ) {
	sn_act_list = new QSNList;
	Q_CHECK_PTR( sn_act_list );
	qAddPostRoutine( sn_cleanup );
    }
}


bool qt_set_socket_handler( int sockfd, int type, QObject *obj, bool enable )
{
    if ( sockfd < 0 || type < 0 || type > 2 || obj == 0 ) {
#if defined(QT_CHECK_RANGE)
	qWarning( "QSocketNotifier: Internal error" );
#endif
	return FALSE;
    }

    QSNList  *list = *sn_vec[type].list;
    fd_set   *fds  =  sn_vec[type].fdspec;
    QSockNot *sn;

    if ( enable ) {				// enable notifier
	if ( !list ) {
	    sn_init();
	    list = new QSNList;			// create new list
	    Q_CHECK_PTR( list );
	    list->setAutoDelete( TRUE );
	    *sn_vec[type].list = list;
	    FD_ZERO( fds );
	    FD_ZERO( sn_vec[type].queue );
	}
	sn = new QSockNot;
	Q_CHECK_PTR( sn );
	sn->obj = obj;
	sn->fd	= sockfd;
	sn->queue = sn_vec[type].queue;
	if ( list->isEmpty() ) {
	    list->insert( 0, sn );
	} else {				// sort list by fd, decreasing
	    QSockNot *p = list->first();
	    while ( p && p->fd > sockfd )
		p = list->next();
#if defined(QT_CHECK_STATE)
	    if ( p && p->fd == sockfd ) {
		static const char *t[] = { "read", "write", "exception" };
		qWarning( "QSocketNotifier: Multiple socket notifiers for "
			 "same socket %d and type %s", sockfd, t[type] );
	    }
#endif
	    if ( p )
		list->insert( list->at(), sn );
	    else
		list->append( sn );
	}
	FD_SET( sockfd, fds );
	sn_highest = QMAX(sn_highest,sockfd);

    } else {					// disable notifier

	if ( list == 0 )
	    return FALSE;			// no such fd set
	QSockNot *sn = list->first();
	while ( sn && !(sn->obj == obj && sn->fd == sockfd) )
	    sn = list->next();
	if ( !sn )				// not found
	    return FALSE;
	FD_CLR( sockfd, fds );			// clear fd bit
	FD_CLR( sockfd, sn->queue );
	if ( sn_act_list )
	    sn_act_list->removeRef( sn );	// remove from activation list
	list->remove();				// remove notifier found above
	if ( sn_highest == sockfd ) {		// find highest fd
	    sn_highest = -1;
	    for ( int i=0; i<3; i++ ) {
		if ( *sn_vec[i].list && (*sn_vec[i].list)->count() )
		    sn_highest = QMAX(sn_highest,  // list is fd-sorted
				      (*sn_vec[i].list)->getFirst()->fd);
	    }
	}
    }

    return TRUE;
}


//
// We choose a random activation order to be more fair under high load.
// If a constant order is used and a peer early in the list can
// saturate the IO, it might grab our attention completely.
// Also, if we're using a straight list, the callback routines may
// delete other entries from the list before those other entries are
// processed.
//

static int sn_activate()
{
    if ( !sn_act_list )
	sn_init();
    int i, n_act = 0;
    for ( i=0; i<3; i++ ) {			// for each list...
	if ( *sn_vec[i].list ) {		// any entries?
	    QSNList  *list = *sn_vec[i].list;
	    fd_set   *fds  = sn_vec[i].fdres;
	    QSockNot *sn   = list->first();
	    while ( sn ) {
		if ( FD_ISSET( sn->fd, fds ) &&	// store away for activation
		     !FD_ISSET( sn->fd, sn->queue ) ) {
		    sn_act_list->insert( (rand() & 0xff) %
					 (sn_act_list->count()+1),
					 sn );
		    FD_SET( sn->fd, sn->queue );
		}
		sn = list->next();
	    }
	}
    }
    if ( sn_act_list->count() > 0 ) {		// activate entries
	QEvent event( QEvent::SockAct );
	QSNListIt it( *sn_act_list );
	QSockNot *sn;
	while ( (sn=it.current()) ) {
	    ++it;
	    sn_act_list->removeRef( sn );
	    if ( FD_ISSET(sn->fd, sn->queue) ) {
		FD_CLR( sn->fd, sn->queue );
		QApplication::sendEvent( sn->obj, &event );
		n_act++;
	    }
	}
    }
    return n_act;
}


/*****************************************************************************
  Main event loop
 *****************************************************************************/

/*!
    Enters the main event loop and waits until exit() is called or the
    main widget is destroyed, and returns the value that was set to
    exit() (which is 0 if exit() is called via quit()).

    It is necessary to call this function to start event handling. The
    main event loop receives events from the window system and
    dispatches these to the application widgets.

    Generally speaking, no user interaction can take place before
    calling exec(). As a special case, modal widgets like QMessageBox
    can be used before calling exec(), because modal widgets call
    exec() to start a local event loop.

    To make your application perform idle processing, i.e. executing a
    special function whenever there are no pending events, use a
    QTimer with 0 timeout. More advanced idle processing schemes can
    be achieved using processEvents().

    \sa quit(), exit(), processEvents(), setMainWidget()
*/

int QApplication::exec()
{
    quit_now = FALSE;
    quit_code = 0;
    enter_loop();

    return quit_code;
}


/*!
    Processes the next event and returns TRUE if there was an event
    (excluding posted events or zero-timer events) to process;
    otherwise returns FALSE.

    This function returns immediately if \a canWait is FALSE. It might
    go into a sleep/wait state if \a canWait is TRUE.

    \sa processEvents()
*/

bool QApplication::processNextEvent( bool canWait )
{
    int	   nevents = 0;

#if defined(QT_THREAD_SUPPORT)
    qApp->lock();
#endif

    emit guiThreadAwake();

    if (qt_is_gui_used ) {
	sendPostedEvents();

	    sendPostedEvents();
	}

    if ( app_exit_loop ) {			// break immediately
#if defined(QT_THREAD_SUPPORT)
	qApp->unlock(FALSE);
#endif

	return FALSE;
    }

    sendPostedEvents();

    static timeval zerotm;
    timeval *tm = qt_wait_timer();		// wait for timer or X event
    if ( !canWait ) {
	if ( !tm )
	    tm = &zerotm;
	tm->tv_sec  = 0;			// no time to wait
	tm->tv_usec = 0;
    }
    if ( sn_highest >= 0 ) {			// has socket notifier(s)
	if ( sn_read )
	    app_readfds = sn_readfds;
	else
	    FD_ZERO( &app_readfds );
	if ( sn_write )
	    app_writefds = sn_writefds;
	if ( sn_except )
	    app_exceptfds = sn_exceptfds;
    } else {
	FD_ZERO( &app_readfds );
    }

    int highest = sn_highest;
    if ( qt_is_gui_used ) {
	FD_SET( app_Xfd, &app_readfds );
	highest = QMAX( highest, app_Xfd );
    }
    int nsel;

#if defined(Q_OS_UNIX)
    FD_SET( qt_thread_pipe[0], &app_readfds );
    highest = QMAX( highest, qt_thread_pipe[0] );
#endif

    if ( qt_preselect_handler ) {
	QVFuncList::Iterator end = qt_preselect_handler->end();
	for ( QVFuncList::Iterator it = qt_preselect_handler->begin(); it != end; ++it )
	    (**it)();
    }

#if defined(QT_THREAD_SUPPORT)
    qApp->unlock(FALSE);
#endif

    nsel = select( highest + 1,
		   &app_readfds,
		   sn_write  ? &app_writefds  : 0,
		   sn_except ? &app_exceptfds : 0,
		   tm );

#undef FDCAST

#if defined(QT_THREAD_SUPPORT)
    qApp->lock();
#endif

#if defined(Q_OS_UNIX)
    if ( nsel > 0 && FD_ISSET( qt_thread_pipe[0], &app_readfds ) ) {
	char c;
	::read(qt_thread_pipe[0], &c, 1);
    }
#endif

    if ( qt_postselect_handler ) {
	QVFuncList::Iterator end = qt_postselect_handler->end();
	for ( QVFuncList::Iterator it = qt_postselect_handler->begin(); it != end; ++it )
	    (**it)();
    }

    if ( nsel == -1 ) {
	if ( errno == EINTR || errno == EAGAIN ) {
	    errno = 0;

#if defined(QT_THREAD_SUPPORT)
	    qApp->unlock(FALSE);
#endif

	    return (nevents > 0);
	} else {
	    ; // select error
	}
    } else if ( nsel > 0 && sn_highest >= 0 ) {
	nevents += sn_activate();
    }

    nevents += qt_activate_timers();		// activate timers

#if defined(QT_THREAD_SUPPORT)
    qApp->unlock(FALSE);
#endif

    return (nevents > 0);
}


void QApplication::wakeUpGuiThread()
{
#if defined(Q_OS_UNIX)
    char c = 0;
    int nbytes;
    if ( ::ioctl(qt_thread_pipe[0], FIONREAD, (char*)&nbytes) >= 0 && nbytes == 0 ) {
	::write(  qt_thread_pipe[1], &c, 1  );
    }
#endif
}


/*!
    \overload

    Processes pending events for \a maxtime milliseconds or until
    there are no more events to process, whichever is shorter.

    You can call this function occasionally when you program is busy
    doing a long operation (e.g. copying a file).

    \sa exec(), QTimer
*/
void QApplication::processEvents( int maxtime )
{
    QTime start = QTime::currentTime();
    QTime now;
    while ( !quit_now && processNextEvent(FALSE) ) {
	now = QTime::currentTime();
	if ( start.msecsTo(now) > maxtime )
	    break;
    }
}

extern uint qGlobalPostedEventsCount();

/*!
    This function returns TRUE if there are pending events; otherwise
    returns FALSE. Pending events can be either from the window system
    or posted events using QApplication::postEvent().
*/
bool QApplication::hasPendingEvents()
{
    return (qGlobalPostedEventsCount());
}

/*!
    This virtual function is only implemented under X11.

    If you create an application that inherits QApplication and
    reimplement this function, you get direct access to all X events
    that the are received from the X server.

    Return TRUE if you want to stop the event from being processed.
    Return FALSE for normal event dispatching.

    \sa x11ProcessEvent()
*/


/*****************************************************************************
  Modal widgets; Since Xlib has little support for this we roll our own
  modal widget mechanism.
  A modal widget without a parent becomes application-modal.
  A modal widget with a parent becomes modal to its parent and grandparents..

  qt_enter_modal()
	Enters modal state
	Arguments:
	    QWidget *widget	A modal widget

  qt_leave_modal()
	Leaves modal state for a widget
	Arguments:
	    QWidget *widget	A modal widget
 *****************************************************************************/

bool qt_modal_state()
{
    return app_do_modal;
}

/*****************************************************************************
  Popup widget mechanism

  openPopup()
	Adds a widget to the list of popup widgets
	Arguments:
	    QWidget *widget	The popup widget to be added

  closePopup()
	Removes a widget from the list of popup widgets
	Arguments:
	    QWidget *widget	The popup widget to be removed
 *****************************************************************************/



/*****************************************************************************
  Timer handling; Xlib has no application timer support so we'll have to
  make our own from scratch.

  NOTE: These functions are for internal use. QObject::startTimer() and
	QObject::killTimer() are for public use.
	The QTimer class provides a high-level interface which translates
	timer events into signals.

  qStartTimer( interval, obj )
	Starts a timer which will run until it is killed with qKillTimer()
	Arguments:
	    int interval	timer interval in milliseconds
	    QObject *obj	where to send the timer event
	Returns:
	    int			timer identifier, or zero if not successful

  qKillTimer( timerId )
	Stops a timer specified by a timer identifier.
	Arguments:
	    int timerId		timer identifier
	Returns:
	    bool		TRUE if successful

  qKillTimer( obj )
	Stops all timers that are sent to the specified object.
	Arguments:
	    QObject *obj	object receiving timer events
	Returns:
	    bool		TRUE if successful
 *****************************************************************************/

//
// Internal data structure for timers
//

struct TimerInfo {				// internal timer info
    int	     id;				// - timer identifier
    timeval  interval;				// - timer interval
    timeval  timeout;				// - when to sent event
    QObject *obj;				// - object to receive event
};

typedef QPtrList<TimerInfo> TimerList;	// list of TimerInfo structs

static QBitArray *timerBitVec;			// timer bit vector
static TimerList *timerList	= 0;		// timer list


//
// Internal operator functions for timevals
//

static inline bool operator<( const timeval &t1, const timeval &t2 )
{
    return t1.tv_sec < t2.tv_sec ||
	  (t1.tv_sec == t2.tv_sec && t1.tv_usec < t2.tv_usec);
}

static inline timeval &operator+=( timeval &t1, const timeval &t2 )
{
    t1.tv_sec += t2.tv_sec;
    if ( (t1.tv_usec += t2.tv_usec) >= 1000000 ) {
	t1.tv_sec++;
	t1.tv_usec -= 1000000;
    }
    return t1;
}

static inline timeval operator+( const timeval &t1, const timeval &t2 )
{
    timeval tmp;
    tmp.tv_sec = t1.tv_sec + t2.tv_sec;
    if ( (tmp.tv_usec = t1.tv_usec + t2.tv_usec) >= 1000000 ) {
	tmp.tv_sec++;
	tmp.tv_usec -= 1000000;
    }
    return tmp;
}

static inline timeval operator-( const timeval &t1, const timeval &t2 )
{
    timeval tmp;
    tmp.tv_sec = t1.tv_sec - t2.tv_sec;
    if ( (tmp.tv_usec = t1.tv_usec - t2.tv_usec) < 0 ) {
	tmp.tv_sec--;
	tmp.tv_usec += 1000000;
    }
    return tmp;
}


//
// Internal functions for manipulating timer data structures.
// The timerBitVec array is used for keeping track of timer identifiers.
//

static int allocTimerId()			// find avail timer identifier
{
    int i = timerBitVec->size()-1;
    while ( i >= 0 && (*timerBitVec)[i] )
	i--;
    if ( i < 0 ) {
	i = timerBitVec->size();
	timerBitVec->resize( 4 * i );
	for( int j=timerBitVec->size()-1; j > i; j-- )
	    timerBitVec->clearBit( j );
    }
    timerBitVec->setBit( i );
    return i+1;
}

static void insertTimer( const TimerInfo *ti )	// insert timer info into list
{
    TimerInfo *t = timerList->first();
    int index = 0;
#if defined(QT_DEBUG)
    int dangerCount = 0;
#endif
    while ( t && t->timeout < ti->timeout ) {	// list is sorted by timeout
#if defined(QT_DEBUG)
	if ( t->obj == ti->obj )
	    dangerCount++;
#endif
	t = timerList->next();
	index++;
    }
    timerList->insert( index, ti );		// inserts sorted
#if defined(QT_DEBUG)
    if ( dangerCount > 16 )
	qDebug( "QObject: %d timers now exist for object %s::%s",
	       dangerCount, ti->obj->className(), ti->obj->name() );
#endif
}

static inline void getTime( timeval &t )	// get time of day
{
	t.tv_sec = time(0);
//    gettimeofday( &t, 0 );
    while ( t.tv_usec >= 1000000 ) {		// NTP-related fix
	t.tv_usec -= 1000000;
	t.tv_sec++;
    }
    while ( t.tv_usec < 0 ) {
	if ( t.tv_sec > 0 ) {
	    t.tv_usec += 1000000;
	    t.tv_sec--;
	} else {
	    t.tv_usec = 0;
	    break;
	}
    }
}

static void repairTimer( const timeval &time )	// repair broken timer
{
    if ( !timerList )				// not initialized
	return;
    timeval diff = watchtime - time;
    register TimerInfo *t = timerList->first();
    while ( t ) {				// repair all timers
	t->timeout = t->timeout - diff;
	t = timerList->next();
    }
}


//
// Timer activation functions (called from the event loop)
//

/*
  Returns the time to wait for the next timer, or null if no timers are
  waiting.

  The result is bounded to qt_wait_timer_max if this exists.
*/

timeval *qt_wait_timer()
{
    static timeval tm;
    bool first = TRUE;
    timeval currentTime;
    if ( timerList && timerList->count() ) {	// there are waiting timers
	getTime( currentTime );
	if ( first ) {
	    if ( currentTime < watchtime )	// clock was turned back
		repairTimer( currentTime );
	    first = FALSE;
	    watchtime = currentTime;
	}
	TimerInfo *t = timerList->first();	// first waiting timer
	if ( currentTime < t->timeout ) {	// time to wait
	    tm = t->timeout - currentTime;
	} else {
	    tm.tv_sec  = 0;			// no time to wait
	    tm.tv_usec = 0;
	}
	if ( qt_wait_timer_max && *qt_wait_timer_max < tm )
	    tm = *qt_wait_timer_max;
	return &tm;
    }
    if ( qt_wait_timer_max ) {
	tm = *qt_wait_timer_max;
	return &tm;
    }
    return 0;					// no timers
}

/*
  Activates the timer events that have expired. Returns the number of timers
  (not 0-timer) that were activated.
*/

int qt_activate_timers()
{
    if ( !timerList || !timerList->count() )	// no timers
	return 0;
    bool first = TRUE;
    timeval currentTime;
    int maxcount = timerList->count();
    int n_act = 0;
    register TimerInfo *t;
    while ( maxcount-- ) {			// avoid starvation
	getTime( currentTime );			// get current time
	if ( first ) {
	    if ( currentTime < watchtime )	// clock was turned back
		repairTimer( currentTime );
	    first = FALSE;
	    watchtime = currentTime;
	}
	t = timerList->first();
	if ( !t || currentTime < t->timeout )	// no timer has expired
	    break;
	timerList->take();			// unlink from list
	t->timeout += t->interval;
	if ( t->timeout < currentTime )
	    t->timeout = currentTime + t->interval;
	insertTimer( t );			// relink timer
	if ( t->interval.tv_usec > 0 || t->interval.tv_sec > 0 )
	    n_act++;
	QTimerEvent e( t->id );
	QApplication::sendEvent( t->obj, &e );	// send event
    }
    return n_act;
}


//
// Timer initialization and cleanup routines
//

static void initTimers()			// initialize timers
{
    timerBitVec = new QBitArray( 128 );
    Q_CHECK_PTR( timerBitVec );
    int i = timerBitVec->size();
    while( i-- > 0 )
	timerBitVec->clearBit( i );
    timerList = new TimerList;
    Q_CHECK_PTR( timerList );
    timerList->setAutoDelete( TRUE );
}

static void cleanupTimers()			// cleanup timer data structure
{
    if ( timerList ) {
	delete timerList;
	timerList = 0;
	delete timerBitVec;
	timerBitVec = 0;
    }
}


//
// Main timer functions for starting and killing timers
//

int qStartTimer( int interval, QObject *obj )
{
    if ( !timerList )				// initialize timer data
	initTimers();
    int id = allocTimerId();			// get free timer id
    if ( id <= 0 ||
	 id > (int)timerBitVec->size() || !obj )// cannot create timer
	return 0;
    timerBitVec->setBit( id-1 );		// set timer active
    TimerInfo *t = new TimerInfo;		// create timer
    Q_CHECK_PTR( t );
    t->id = id;
    t->interval.tv_sec  = interval/1000;
    t->interval.tv_usec = (interval%1000)*1000;
    timeval currentTime;
    getTime( currentTime );
    t->timeout = currentTime + t->interval;
    t->obj = obj;
    insertTimer( t );				// put timer in list
    return id;
}

bool qKillTimer( int id )
{
    register TimerInfo *t;
    if ( !timerList || id <= 0 ||
	 id > (int)timerBitVec->size() || !timerBitVec->testBit( id-1 ) )
	return FALSE;				// not init'd or invalid timer
    t = timerList->first();
    while ( t && t->id != id )			// find timer info in list
	t = timerList->next();
    if ( t ) {					// id found
	timerBitVec->clearBit( id-1 );		// set timer inactive
	return timerList->remove();
    }
    else					// id not found
	return FALSE;
}

bool qKillTimer( QObject *obj )
{
    register TimerInfo *t;
    if ( !timerList )				// not initialized
	return FALSE;
    t = timerList->first();
    while ( t ) {				// check all timers
	if ( t->obj == obj ) {			// object found
	    timerBitVec->clearBit( t->id-1 );
	    timerList->remove();
	    t = timerList->current();
	} else {
	    t = timerList->next();
	}
    }
    return TRUE;
}



//
// XInput Translation Event
//
#if defined (QT_TABLET_SUPPORT)
bool QETWidget::translateXinputEvent( const XEvent *ev )
{
    // Wacom has put defines in their wacom.h file so it would be quite wise
    // to use them, need to think of a decent way of getting rid of not using
    // it when it doesn't exist...
    XDeviceState *s;
    XInputClass *iClass;
    XValuatorState *vs;
    QWidget *w = this;
    QPoint global,
	curr;
    int j,
	pressure = 0,
	xTilt = 0,
	yTilt = 0,
	deviceType = QTabletEvent::NoDevice;
    QPair<int, int> tId;
    XDevice *dev;
    XDeviceMotionEvent *motion;

    motion = (XDeviceMotionEvent*)ev;
#if defined(Q_OS_IRIX)
    // default...
    dev = devStylus;
#else
    if ( motion->deviceid == devStylus->device_id ) {
	dev = devStylus;
	deviceType = QTabletEvent::Stylus;
    } else if ( motion->deviceid == devEraser->device_id ) {
	dev = devEraser;
	deviceType = QTabletEvent::Eraser;
    }
#endif

    const int PRESSURE_LEVELS = 255;
    // we got the maximum pressure at start time, since various tablets have
    // varying levels of distinguishing pressure changes, let's standardize and
    // scale everything to 256 different levels...
    static int scaleFactor = -1;
    if ( scaleFactor == -1 ) {
	if ( max_pressure > PRESSURE_LEVELS )
	    scaleFactor = max_pressure / PRESSURE_LEVELS;
	else
	    scaleFactor = PRESSURE_LEVELS / max_pressure;
    }
    // Heh, we got an event from the stylus, but on Irix the event doesn't
    // give us all the information we need (some of the values are horibbly
    // wrong ),  It is slightly better to query the device state and get the
    // real state of the valuators...
    s = XQueryDeviceState( appDpy, dev );
    if ( s == NULL )
	return FALSE;
    iClass = s->data;
    for ( j = 0; j < s->num_classes; j++ ) {
	if ( iClass->c_class == ValuatorClass ) {
	    vs = (XValuatorState *)iClass;
	    // figure out what device we have, based on bitmasking...
#if defined (Q_OS_IRIX)
	    if ( vs->valuators[WAC_TRANSDUCER_I]
		 & WAC_TRANSDUCER_PROX_MSK ) {
		switch ( vs->valuators[WAC_TRANSDUCER_I]
			 & WAC_TRANSDUCER_MSK ) {
		case WAC_PUCK_ID:
		    deviceType = QTabletEvent::Puck;
		    break;
		case WAC_STYLUS_ID:
		    deviceType = QTabletEvent::Stylus;
		    break;
		case WAC_ERASER_ID:
		    deviceType = QTabletEvent::Eraser;
		    break;
		}
		// Get a Unique Id for the device, Wacom gives us this ability
 		tId.first = vs->valuators[WAC_TRANSDUCER_I] & WAC_TRANSDUCER_ID_MSK;
		tId.second = vs->valuators[WAC_SERIAL_NUM_I];
	    } else
		deviceType = QTabletEvent::NoDevice;
	    // apparently Wacom needs a cast for the +/- values to make sense
	    xTilt = short(vs->valuators[WAC_XTILT_I]);
	    yTilt = short(vs->valuators[WAC_YTILT_I]);
	    if ( max_pressure > PRESSURE_LEVELS )
		pressure = vs->valuators[WAC_PRESSURE_I] / scaleFactor;
	    else
		pressure = vs->valuators[WAC_PRESSURE_I] * scaleFactor;
	    // why not use the high res values for global?
	    global = QPoint( vs->valuators[WAC_XCOORD_I],
			     vs->valuators[WAC_YCOORD_I] );
#else
	    xTilt = short(vs->valuators[3]);
	    yTilt = short(vs->valuators[4]);
	    if ( max_pressure > PRESSURE_LEVELS )
		pressure = vs->valuators[2] / scaleFactor;
	    else
		pressure = vs->valuators[2] * scaleFactor;
	    global = QPoint( vs->valuators[0], vs->valuators[1] );
	    // The only way to get these Ids is to scan the XFree86 log, which I'm not going to do.
	    tId.first = tId.second = -1;
#endif
	}
	iClass = (XInputClass*)((char*)iClass + iClass->length);
    }
    curr = QPoint( motion->x, motion->y );

    QTabletEvent e( curr, global, deviceType, pressure, xTilt, yTilt, tId );
    QApplication::sendSpontaneousEvent( w, &e );
    XFreeDeviceState( s );
    return TRUE;
}
#endif

#ifndef XK_ISO_Left_Tab
#define	XK_ISO_Left_Tab					0xFE20
#endif

#if !defined(QT_NO_XIM)
static const unsigned short cyrillicKeysymsToUnicode[] = {
    0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457,
    0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0000, 0x045e, 0x045f,
    0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407,
    0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0000, 0x040e, 0x040f,
    0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
    0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,
    0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
    0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a,
    0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
    0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e,
    0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
    0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a
};

static const unsigned short greekKeysymsToUnicode[] = {
    0x0000, 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c,
    0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015,
    0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc,
    0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000,
    0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
    0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
    0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
    0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
    0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,
    0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
    0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7,
    0x03c8, 0x03c9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};

// ### add keysym conversion for the missing ranges.
static QChar keysymToUnicode(unsigned char byte3, unsigned char byte4)
{
    if ( byte3 == 0x06 ) {
	// russian, use lookup table
	if ( byte4 > 0xa0 )
	    return QChar( cyrillicKeysymsToUnicode[byte4 - 0xa0] );
    }
    if ( byte3 == 0x07 ) {
	// greek
	if ( byte4 > 0xa0 )
	    return QChar( greekKeysymsToUnicode[byte4 - 0xa0] );
    }
    return QChar(0x0);
}
#endif



// declared above: static QPtrList<QScrollInProgress> *sip_list = 0;


/*!
    Sets the text cursor's flash (blink) time to \a msecs
    milliseconds. The flash time is the time required to display,
    invert and restore the caret display. Usually the text cursor is
    displayed for \a msecs/2 milliseconds, then hidden for \a msecs/2
    milliseconds, but this may vary.

    Note that on Microsoft Windows, calling this function sets the
    cursor flash time for all windows.

    \sa cursorFlashTime()
*/
void  QApplication::setCursorFlashTime( int msecs )
{
    cursor_flash_time = msecs;
}


/*!
    Returns the text cursor's flash (blink) time in milliseconds. The
    flash time is the time required to display, invert and restore the
    caret display.

    The default value on X11 is 1000 milliseconds. On Windows, the
    control panel value is used.

    Widgets should not cache this value since it may be changed at any
    time by the user changing the global desktop settings.

    \sa setCursorFlashTime()
*/
int QApplication::cursorFlashTime()
{
    return cursor_flash_time;
}

/*!
    Sets the time limit that distinguishes a double click from two
    consecutive mouse clicks to \a ms milliseconds.

    Note that on Microsoft Windows, calling this function sets the
    double click interval for all windows.

    \sa doubleClickInterval()
*/

void QApplication::setDoubleClickInterval( int ms )
{
    mouse_double_click_time = ms;
}


/*!
    Returns the maximum duration for a double click.

    The default value on X11 is 400 milliseconds. On Windows, the
    control panel value is used.

    \sa setDoubleClickInterval()
*/

int QApplication::doubleClickInterval()
{
    return mouse_double_click_time;
}


/*!
    Enables the UI effect \a effect if \a enable is TRUE, otherwise
    the effect will not be used.

    \sa isEffectEnabled(), Qt::UIEffect, setDesktopSettingsAware()
*/
void QApplication::setEffectEnabled( Qt::UIEffect effect, bool enable )
{
    switch (effect) {
    case UI_AnimateMenu:
	animate_menu = enable;
	break;
    case UI_FadeMenu:
	if ( enable )
	    animate_menu = TRUE;
	fade_menu = enable;
	break;
    case UI_AnimateCombo:
	animate_combo = enable;
	break;
    case UI_AnimateTooltip:
	animate_tooltip = enable;
	break;
    case UI_FadeTooltip:
	if ( enable )
	    animate_tooltip = TRUE;
	fade_tooltip = enable;
	break;
    default:
	animate_ui = enable;
	break;
    }
}

/*****************************************************************************
  Session management support
 *****************************************************************************/
#define QT_NO_SM_SUPPORT
#ifndef QT_NO_SM_SUPPORT

#include <X11/SM/SMlib.h>

class QSmSocketReceiver : public QObject
{
    Q_OBJECT
public:
    QSmSocketReceiver( int socket )
	: QObject(0,0)
	{
	    QSocketNotifier* sn = new QSocketNotifier( socket, QSocketNotifier::Read, this );
	    connect( sn, SIGNAL( activated(int) ), this, SLOT( socketActivated(int) ) );
	}

public slots:
     void socketActivated(int);
};


static SmcConn smcConnection = 0;
static bool sm_interactionActive;
static bool sm_smActive;
static int sm_interactStyle;
static int sm_saveType;
static bool sm_cancel;
// static bool sm_waitingForPhase2;  ### never used?!?
static bool sm_waitingForInteraction;
static bool sm_isshutdown;
// static bool sm_shouldbefast;  ### never used?!?
static bool sm_phase2;
static bool sm_in_phase2;

static QSmSocketReceiver* sm_receiver = 0;

static void resetSmState();
static void sm_setProperty( const char* name, const char* type,
			    int num_vals, SmPropValue* vals);
static void sm_saveYourselfCallback( SmcConn smcConn, SmPointer clientData,
				  int saveType, Bool shutdown , int interactStyle, Bool fast);
static void sm_saveYourselfPhase2Callback( SmcConn smcConn, SmPointer clientData ) ;
static void sm_dieCallback( SmcConn smcConn, SmPointer clientData ) ;
static void sm_shutdownCancelledCallback( SmcConn smcConn, SmPointer clientData );
static void sm_saveCompleteCallback( SmcConn smcConn, SmPointer clientData );
static void sm_interactCallback( SmcConn smcConn, SmPointer clientData );
static void sm_performSaveYourself( QSessionManager* );

static void resetSmState()
{
//    sm_waitingForPhase2 = FALSE; ### never used?!?
    sm_waitingForInteraction = FALSE;
    sm_interactionActive = FALSE;
    sm_interactStyle = SmInteractStyleNone;
    sm_smActive = FALSE;
    sm_blockUserInput = FALSE;
    sm_isshutdown = FALSE;
//    sm_shouldbefast = FALSE; ### never used?!?
    sm_phase2 = FALSE;
    sm_in_phase2 = FALSE;
}


// theoretically it's possible to set several properties at once. For
// simplicity, however, we do just one property at a time
static void sm_setProperty( const char* name, const char* type,
			    int num_vals, SmPropValue* vals)
{
    if (num_vals ) {
      SmProp prop;
      prop.name = (char*)name;
      prop.type = (char*)type;
      prop.num_vals = num_vals;
      prop.vals = vals;

      SmProp* props[1];
      props[0] = &prop;
      SmcSetProperties( smcConnection, 1, props );
    }
    else {
      char* names[1];
      names[0] = (char*) name;
      SmcDeleteProperties( smcConnection, 1, names );
    }
}

static void sm_setProperty( const QString& name, const QString& value)
{
    SmPropValue prop;
    prop.length = value.length();
    prop.value = (SmPointer) value.latin1();
    sm_setProperty( name.latin1(), SmARRAY8, 1, &prop );
}

static void sm_setProperty( const QString& name, const QStringList& value)
{
    SmPropValue *prop = new SmPropValue[ value.count() ];
    int count = 0;
    for ( QStringList::ConstIterator it = value.begin(); it != value.end(); ++it ) {
      prop[ count ].length = (*it).length();
      prop[ count ].value = (char*)(*it).latin1();
      ++count;
    }
    sm_setProperty( name.latin1(), SmLISTofARRAY8, count, prop );
    delete [] prop;
}


// workaround for broken libsm, see below
struct QT_smcConn {
    unsigned int save_yourself_in_progress : 1;
    unsigned int shutdown_in_progress : 1;
};

static void sm_saveYourselfCallback( SmcConn smcConn, SmPointer clientData,
				  int saveType, Bool shutdown , int interactStyle, Bool /*fast*/)
{
    if (smcConn != smcConnection )
	return;
    sm_cancel = FALSE;
    sm_smActive = TRUE;
    sm_isshutdown = shutdown;
    sm_saveType = saveType;
    sm_interactStyle = interactStyle;
//    sm_shouldbefast = fast; ### never used?!?

    // ugly workaround for broken libSM. libSM should do that _before_
    // actually invoking the callback in sm_process.c
    ( (QT_smcConn*)smcConn )->save_yourself_in_progress = TRUE;
    if ( sm_isshutdown )
	( (QT_smcConn*)smcConn )->shutdown_in_progress = TRUE;

    sm_performSaveYourself( (QSessionManager*) clientData );
    if ( !sm_isshutdown ) // we cannot expect a confirmation message in that case
	resetSmState();
}

static void sm_performSaveYourself( QSessionManager* sm )
{
    if ( sm_isshutdown )
	sm_blockUserInput = TRUE;

    // tell the session manager about our program in best POSIX style
    sm_setProperty( SmProgram, QString( qApp->argv()[0] ) );
    // tell the session manager about our user as well.
    struct passwd* entry = getpwuid( geteuid() );
    if ( entry )
	sm_setProperty( SmUserID, QString::fromLatin1( entry->pw_name ) );

    // generate a restart and discard command that makes sense
    QStringList restart;
    restart  << qApp->argv()[0] << "-session" << sm->sessionId();
    sm->setRestartCommand( restart );
    QStringList discard;
    sm->setDiscardCommand( discard );

    switch ( sm_saveType ) {
    case SmSaveBoth:
	qApp->commitData( *sm );
	if ( sm_isshutdown && sm_cancel)
	    break; // we cancelled the shutdown, no need to save state
    // fall through
    case SmSaveLocal:
	qApp->saveState( *sm );
	break;
    case SmSaveGlobal:
	qApp->commitData( *sm );
	break;
    default:
	break;
    }

    if ( sm_phase2 && !sm_in_phase2 ) {
	SmcRequestSaveYourselfPhase2( smcConnection, sm_saveYourselfPhase2Callback, (SmPointer*) sm );
	sm_blockUserInput = FALSE;
    }
    else {
	// close eventual interaction monitors and cancel the
	// shutdown, if required. Note that we can only cancel when
	// performing a shutdown, it does not work for checkpoints
	if ( sm_interactionActive ) {
	    SmcInteractDone( smcConnection, sm_isshutdown && sm_cancel);
	    sm_interactionActive = FALSE;
	}
	else if ( sm_cancel && sm_isshutdown ) {
	    if ( sm->allowsErrorInteraction() ) {
		SmcInteractDone( smcConnection, True );
		sm_interactionActive = FALSE;
	    }
	}

	// set restart and discard command in session manager
	sm_setProperty( SmRestartCommand, sm->restartCommand() );
	sm_setProperty( SmDiscardCommand, sm->discardCommand() );

	// set the restart hint
	SmPropValue prop;
	prop.length = sizeof( int );
	int value = sm->restartHint();
	prop.value = (SmPointer) &value;
	sm_setProperty( SmRestartStyleHint, SmCARD8, 1, &prop );

	// we are done
	SmcSaveYourselfDone( smcConnection, !sm_cancel );
    }
}

static void sm_dieCallback( SmcConn smcConn, SmPointer /* clientData  */)
{
    if (smcConn != smcConnection )
	return;
    resetSmState();
    qApp->quit();
}

static void sm_shutdownCancelledCallback( SmcConn smcConn, SmPointer /* clientData */)
{
    if (smcConn != smcConnection )
	return;
    if ( sm_waitingForInteraction )
	qApp->exit_loop();
    resetSmState();
}

static void sm_saveCompleteCallback( SmcConn smcConn, SmPointer /*clientData */)
{
    if (smcConn != smcConnection )
	return;
    resetSmState();
}

static void sm_interactCallback( SmcConn smcConn, SmPointer /* clientData */ )
{
    if (smcConn != smcConnection )
	return;
    if ( sm_waitingForInteraction )
	qApp->exit_loop();
}

static void sm_saveYourselfPhase2Callback( SmcConn smcConn, SmPointer clientData )
{
    if (smcConn != smcConnection )
	return;
    sm_in_phase2 = TRUE;
    sm_performSaveYourself( (QSessionManager*) clientData );
}


void QSmSocketReceiver::socketActivated(int)
{
    IceProcessMessages( SmcGetIceConnection( smcConnection ), 0, 0);
}


#undef Bool
#include "qapplication_x11.moc"

class QSessionManagerData
{
public:
    QStringList restartCommand;
    QStringList discardCommand;
    QString sessionId;
    QSessionManager::RestartHint restartHint;
};

QSessionManager::QSessionManager( QApplication * app, QString &session )
    : QObject( app, "session manager" )
{
    d = new QSessionManagerData;
    d->sessionId = session;
    d->restartHint = RestartIfRunning;

    resetSmState();
    char cerror[256];
    char* myId = 0;
    char* prevId = (char*)session.latin1(); // we know what we are doing

    SmcCallbacks cb;
    cb.save_yourself.callback = sm_saveYourselfCallback;
    cb.save_yourself.client_data = (SmPointer) this;
    cb.die.callback = sm_dieCallback;
    cb.die.client_data = (SmPointer) this;
    cb.save_complete.callback = sm_saveCompleteCallback;
    cb.save_complete.client_data = (SmPointer) this;
    cb.shutdown_cancelled.callback = sm_shutdownCancelledCallback;
    cb.shutdown_cancelled.client_data = (SmPointer) this;

    // avoid showing a warning message below
    const char* session_manager = getenv("SESSION_MANAGER");
    if ( !session_manager || !session_manager[0] )
	return;

    smcConnection = SmcOpenConnection( 0, 0, 1, 0,
				       SmcSaveYourselfProcMask |
				       SmcDieProcMask |
				       SmcSaveCompleteProcMask |
				       SmcShutdownCancelledProcMask,
				       &cb,
				       prevId,
				       &myId,
				       256, cerror );

    d->sessionId = QString::fromLatin1( myId );
    ::free( myId ); // it was allocated by C
    session = d->sessionId;

    QString error = cerror;
    if (!smcConnection ) {
	qWarning("Session management error: %s", error.latin1() );
    }
    else {
	sm_receiver = new QSmSocketReceiver(  IceConnectionNumber( SmcGetIceConnection( smcConnection ) ) );
    }
}

QSessionManager::~QSessionManager()
{
    if ( smcConnection )
      SmcCloseConnection( smcConnection, 0, 0 );
    smcConnection = 0;
    delete sm_receiver;
    delete d;
}

QString QSessionManager::sessionId() const
{
    return d->sessionId;
}

void* QSessionManager::handle() const
{
    return (void*) smcConnection;
}


bool QSessionManager::allowsInteraction()
{
    if ( sm_interactionActive )
	return TRUE;

    if ( sm_waitingForInteraction )
	return FALSE;

    if ( sm_interactStyle == SmInteractStyleAny ) {
	sm_waitingForInteraction =  SmcInteractRequest( smcConnection, SmDialogNormal,
							sm_interactCallback, (SmPointer*) this );
    }
    if ( sm_waitingForInteraction ) {
	qApp->enter_loop();
	sm_waitingForInteraction = FALSE;
	if ( sm_smActive ) { // not cancelled
	    sm_interactionActive = TRUE;
	    sm_blockUserInput = FALSE;
	    return TRUE;
	}
    }
    return FALSE;
}

bool QSessionManager::allowsErrorInteraction()
{
    if ( sm_interactionActive )
	return TRUE;

    if ( sm_waitingForInteraction )
	return FALSE;

    if ( sm_interactStyle == SmInteractStyleAny || sm_interactStyle == SmInteractStyleErrors ) {
	sm_waitingForInteraction =  SmcInteractRequest( smcConnection, SmDialogError,
							sm_interactCallback, (SmPointer*) this );
    }
    if ( sm_waitingForInteraction ) {
	qApp->enter_loop();
	sm_waitingForInteraction = FALSE;
	if ( sm_smActive ) { // not cancelled
	    sm_interactionActive = TRUE;
	    sm_blockUserInput = FALSE;
	    return TRUE;
	}
    }
    return FALSE;
}

void QSessionManager::release()
{
    if ( sm_interactionActive ) {
	SmcInteractDone( smcConnection, False );
	sm_interactionActive = FALSE;
	if ( sm_smActive && sm_isshutdown )
	    sm_blockUserInput = TRUE;
    }
}

void QSessionManager::cancel()
{
    sm_cancel = TRUE;
}

void QSessionManager::setRestartHint( QSessionManager::RestartHint hint)
{
    d->restartHint = hint;
}

QSessionManager::RestartHint QSessionManager::restartHint() const
{
    return d->restartHint;
}

void QSessionManager::setRestartCommand( const QStringList& command)
{
    d->restartCommand = command;
}

QStringList QSessionManager::restartCommand() const
{
    return d->restartCommand;
}

void QSessionManager::setDiscardCommand( const QStringList& command)
{
    d->discardCommand = command;
}

QStringList QSessionManager::discardCommand() const
{
    return d->discardCommand;
}

void QSessionManager::setManagerProperty( const QString& name, const QString& value)
{
    SmPropValue prop;
    prop.length = value.length();
    prop.value = (SmPointer) value.utf8().data();
    sm_setProperty( name.latin1(), SmARRAY8, 1, &prop );
}

void QSessionManager::setManagerProperty( const QString& name, const QStringList& value)
{
    SmPropValue *prop = new SmPropValue[ value.count() ];
    int count = 0;
    for ( QStringList::ConstIterator it = value.begin(); it != value.end(); ++it ) {
      prop[ count ].length = (*it).length();
      prop[ count ].value = (char*)(*it).utf8().data();
      ++count;
    }
    sm_setProperty( name.latin1(), SmLISTofARRAY8, count, prop );
    delete [] prop;
}

bool QSessionManager::isPhase2() const
{
    return sm_in_phase2;
}

void QSessionManager::requestPhase2()
{
    sm_phase2 = TRUE;
}


#endif // QT_NO_SM_SUPPORT
