/* 
 * Copyright (C) 2000-2004 the xine project
 * 
 * This file is part of xine, a unix video player.
 * 
 * xine is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * xine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
 *
 * $Id: xitk.c,v 1.146 2006/02/19 02:52:29 fsck-p Exp $
 *
 */
#define _XITK_C_

/* required to enable POSIX variant of getpwuid_r on solaris */
#define _POSIX_PTHREAD_SEMANTICS 1


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <pwd.h>
#include <errno.h>
#include <inttypes.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>

#ifdef HAVE_SETLOCALE
#include <locale.h>
#endif

#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>

#include "_xitk.h"

extern char **environ;
#undef TRACE_LOCKS

#ifdef TRACE_LOCKS
static int ml = 0;
#define MUTLOCK()                                                             \
  {                                                                           \
    int i;                                                                    \
    ml++;                                                                     \
    for(i=0; i<ml; i++) printf(".");                                          \
    printf("LOCK\n");                                                         \
    pthread_mutex_lock(&gXitk->mutex);                                        \
  }

#define MUTUNLOCK()                                                           \
  {                                                                           \
    int i;                                                                    \
    for(i=0; i<ml; i++) printf(".");                                          \
    printf("UNLOCK\n");                                                       \
    ml--;                                                                     \
    pthread_mutex_unlock(&gXitk->mutex);                                      \
  }

#else
#define MUTLOCK()   { pthread_mutex_lock(&gXitk->mutex); }
#define MUTUNLOCK() { pthread_mutex_unlock(&gXitk->mutex); }
#endif

#define FXLOCK(_fx) { pthread_mutex_lock(&_fx->mutex); }
#define FXUNLOCK(_fx) { pthread_mutex_unlock(&_fx->mutex); }


typedef struct {
  Window                      window;
  Atom                        XA_XITK;

  xitk_move_t                 move;

  struct {
    int                       x;
    int                       y;
  } old_pos;

  struct {
    int                       x;
    int                       y;
  } new_pos;

  int                         width;
  int                         height;

  XEvent                     *old_event;

  xitk_widget_list_t         *widget_list;
  char                       *name;
  widget_event_callback_t     xevent_callback;
  widget_newpos_callback_t    newpos_callback;
  xitk_register_key_t         key;
  xitk_dnd_t                 *xdnd;
  void                       *user_data;

  pthread_mutex_t             mutex;
  int                         destroy;
} __gfx_t;

typedef struct {
  Display                    *display;
  XColor                      black;
  int                         display_width;
  int                         display_height;
  int                         verbosity;
  xitk_list_t                *list;
  xitk_list_t                *gfx;
  int                         use_xshm;

  uint32_t                    wm_type;

  int                        (*x_error_handler)(Display *, XErrorEvent *);

  pthread_mutex_t             mutex;
  int                         running;
  xitk_register_key_t         key;
  xitk_config_t              *config;
  xitk_signal_callback_t      sig_callback;
  void                       *sig_data;

  Window                      modalw;
  xitk_widget_t              *menu;
  
  struct timeval              keypress;

  KeyCode                     ignore_keys[2];

  pthread_t                  *tips_thread;
  unsigned long               tips_timeout;

  struct {
    Window                    window;
    int                       focus;
  } parent;
  
} __xitk_t;

static __xitk_t    *gXitk;
static pid_t        xitk_pid;
static Atom XA_WIN_LAYER = None, XA_STAYS_ON_TOP = None;
static Atom XA_NET_WM_STATE = None, XA_NET_WM_STATE_ABOVE = None;
static Atom XA_NET_WM_STATE_FULLSCREEN = None;

static Atom XA_WM_WINDOW_TYPE = None;
static Atom XA_WM_WINDOW_TYPE_DESKTOP = None;
static Atom XA_WM_WINDOW_TYPE_DOCK = None;
static Atom XA_WM_WINDOW_TYPE_TOOLBAR = None;
static Atom XA_WM_WINDOW_TYPE_MENU = None;
static Atom XA_WM_WINDOW_TYPE_UTILITY = None;
static Atom XA_WM_WINDOW_TYPE_SPLASH = None;
static Atom XA_WM_WINDOW_TYPE_DIALOG = None;
static Atom XA_WM_WINDOW_TYPE_NORMAL = None;

void widget_stop(void);

void xitk_modal_window(Window w) {
  gXitk->modalw = w;
}
void xitk_unmodal_window(Window w) {
  if(w == gXitk->modalw)
    gXitk->modalw = None;
}

/*
 * Execute a shell command.
 */
int xitk_system(int dont_run_as_root, char *command) {
  int pid, status;
  
  /* 
   * Don't permit run as root
   */
  if(dont_run_as_root) {
    if(getuid() == 0)
      return -1;
  }
  
  if(command == 0)
    return 1;
  
  pid = fork();
  
  if(pid == -1)
    return -1;
  
  if(pid == 0) {
    char *argv[4];
    argv[0] = "sh";
    argv[1] = "-c";
    argv[2] = command;
    argv[3] = 0;
    execve("/bin/sh", argv, environ);
    exit(127);
  }
  
  do {
    if(waitpid(pid, &status, 0) == -1) {
      if (errno != EINTR)
	return -1;
    } 
    else {
      return WEXITSTATUS(status);
    }
  } while(1);
  
  return -1;
}

/*
 * A thread-safe usecond sleep
 */
void xitk_usec_sleep(unsigned long usec) {
#ifdef HAVE_NANOSLEEP
  /* nanosleep is prefered on solaris, because it's mt-safe */
  struct timespec ts, remaining;
  
  ts.tv_sec =   usec / 1000000;
  ts.tv_nsec = (usec % 1000000) * 1000;
  while (nanosleep (&ts, &remaining) == -1 && errno == EINTR)
    ts = remaining;
#else
  usleep(usec);
#endif
}

static int _x_error_handler(Display *display, XErrorEvent *xevent) {
  char buffer[2048];
  
  XGetErrorText(display, xevent->error_code, &buffer[0], 1023);
  
  XITK_WARNING("X error received: '%s'\n", buffer);
  
  xitk_x_error = 1;
  return 0;
  
}

void xitk_set_current_menu(xitk_widget_t *menu) {
  if(gXitk->menu)
    xitk_menu_destroy(gXitk->menu);

  gXitk->menu = menu;
}
void xitk_unset_current_menu(void) {
  gXitk->menu = NULL;
}

int xitk_install_x_error_handler(void) {
  if(gXitk->x_error_handler == NULL) {
    gXitk->x_error_handler = XSetErrorHandler(_x_error_handler);
    XSync(gXitk->display, False);
    return 1;
  }
  return 0;
}

int xitk_uninstall_x_error_handler(void) {
  if(gXitk->x_error_handler != NULL) {
    XSetErrorHandler(gXitk->x_error_handler);
    gXitk->x_error_handler = NULL;
    XSync(gXitk->display, False);
    return 1;
  }
  return 0;
}

/*
 *
 */
static void xitk_signal_handler(int sig) {
  pid_t cur_pid = getppid();

  /* First, call registered handler */
  if(cur_pid == xitk_pid) {
    if(gXitk->sig_callback)
      gXitk->sig_callback(sig, gXitk->sig_data);
  }
  
  gXitk->running = 0;
  
  switch (sig) {
    
  case SIGINT:
  case SIGTERM:
  case SIGQUIT:
    if(cur_pid == xitk_pid) {
      
      xitk_list_free(gXitk->list);
      xitk_list_free(gXitk->gfx);
      xitk_config_deinit(gXitk->config);
      
      XITK_FREE(gXitk);
      exit(1);
    }
    break;

  case SIGSEGV:
    {
      fprintf(stderr, "xiTK received SIGSEGV signal, RIP.\n");
#ifndef DEBUG
      abort();
#elif defined(HAVE_EXECINFO_H)
      void    *backtrace_array[255];
      char   **backtrace_strings;
      int      entries, i;
      
      if((entries = backtrace(backtrace_array, 255))) {
	if((backtrace_strings = backtrace_symbols(backtrace_array, entries))) {
	  printf("Backtrace:\n");
	  
	  for(i = 0; i < entries; i++) {
	    printf("  [%d] %s\n", i, backtrace_strings[i]);
	  }
	  
	  free(backtrace_strings);
	  printf("--\n");
	}
      }
#endif
    }
    break;


  }
}

/* 
 * Desc: query the server for support for the MIT_SHM extension
 * Return:  0 = not available
 *          1 = shared XImage support available
 *          2 = shared Pixmap support available also
 * Shamelessly stolen from gdk, and slightly tuned for xitk.
 */
static int xitk_check_xshm(Display *display) {
#ifdef HAVE_SHM
  if(XShmQueryExtension(display)) {
    int major, minor, ignore;
    Bool pixmaps;
    
    if(XQueryExtension(display, "MIT-SHM", &ignore, &ignore, &ignore)) {
      if(XShmQueryVersion(display, &major, &minor, &pixmaps ) == True) {
	if((pixmaps == True) && ((XShmPixmapFormat(display)) == ZPixmap))
	  return 2;
	else if(pixmaps == True)
	  return 1;
      }
    }
  }
#endif
  return 0;
}
int xitk_is_use_xshm(void) {
  return gXitk->use_xshm;
}

static char *get_wm_name(Display *display, Window win, char *atom_name) {
  char *wm_name = NULL;
  Atom  atom;

  /* Extract WM Name */	
  if((atom = XInternAtom(display, atom_name, True)) != None) {
    unsigned char   *prop_return = NULL;
    unsigned long    nitems_return;
    unsigned long    bytes_after_return;
    Atom             type_return, type_utf8;
    int              format_return;
    
    if((XGetWindowProperty(display, win, atom, 0, 4, False, XA_STRING,
			   &type_return, &format_return, &nitems_return,
			   &bytes_after_return, &prop_return)) == Success) {
      
      if(type_return != None) {
	if(type_return != XA_STRING) {
	  type_utf8 = XInternAtom(display, "UTF8_STRING", True);
	  if(type_utf8 != None) {
	    
	    if(type_return == type_utf8) {
	      
	      if(prop_return)
		XFree(prop_return);
	      prop_return = NULL;
	      
	      if((XGetWindowProperty(display, win, atom, 0, 4, False, type_utf8,
				     &type_return, &format_return, &nitems_return,
				     &bytes_after_return, &prop_return)) == Success) {
		
		if(format_return == 8)
		  wm_name = strdup((char *)prop_return);

	      }
	    }
	    else if(format_return == 8)
	      wm_name = strdup((char *)prop_return);

	  }
	}
	else
	  wm_name = strdup((char *)prop_return);

      }
      
      if(prop_return)
	XFree(prop_return);
    }
  }
  
  return wm_name;
}
static uint32_t xitk_check_wm(Display *display) {
  Atom      atom;
  uint32_t  type = WM_TYPE_UNKNOWN;
  char     *wm_name = NULL;
  
  XLockDisplay(display);

  if((atom = XInternAtom(display, "XFWM_FLAGS", True)) != None) {
    type |= WM_TYPE_XFCE;
  }
  else if((atom = XInternAtom(display, "_WINDOWMAKER_WM_PROTOCOLS", True)) != None) {
    type |= WM_TYPE_WINDOWMAKER;
  }
  else if((atom = XInternAtom(display, "_SAWMILL_TIMESTAMP", True)) != None) {
    type |= WM_TYPE_SAWFISH;
  }
  else if((atom = XInternAtom(display, "ENLIGHTENMENT_COMMS", True)) != None) {
    type |= WM_TYPE_E;
  }
  else if((atom = XInternAtom(display, "_METACITY_RESTART_MESSAGE", True)) != None) {
    type |= WM_TYPE_METACITY;
  }
  else if((atom = XInternAtom(display, "_AS_STYLE", True)) != None) {
    type |= WM_TYPE_AFTERSTEP;
  }
  else if((atom = XInternAtom(display, "_ICEWM_WINOPTHINT", True)) != None) {
    type |= WM_TYPE_ICE;
  }
  else if((atom = XInternAtom(display, "_BLACKBOX_HINTS", True)) != None) {
    type |= WM_TYPE_BLACKBOX;
  }
  else if((atom = XInternAtom(display, "LARSWM_EXIT", True)) != None) {
    type |= WM_TYPE_LARSWM;
  }
  else if(((atom = XInternAtom(display, "KWIN_RUNNING", True)) != None) &&
	  ((atom = XInternAtom(display, "_DT_SM_WINDOW_INFO", True)) != None)) {
    type |= WM_TYPE_KWIN;
  }
  else if((atom = XInternAtom(display, "DTWM_IS_RUNNING", True)) != None) {
    type |= WM_TYPE_DTWM;
  }

  if((atom = XInternAtom(display, "_WIN_SUPPORTING_WM_CHECK", True)) != None) {
    unsigned char   *prop_return = NULL;
    unsigned long    nitems_return;
    unsigned long    bytes_after_return;
    Atom             type_return;
    int              format_return;
    Status           status;
    
    /* Check for Gnome Compliant WM */
    if((XGetWindowProperty(display, (XDefaultRootWindow(display)), atom, 0,
			   1, False, XA_CARDINAL,
			   &type_return, &format_return, &nitems_return,
			   &bytes_after_return, &prop_return)) == Success) {

      if((type_return != None) && (type_return == XA_CARDINAL) &&
	 ((format_return == 32) && (nitems_return == 1) && (bytes_after_return == 0))) {
	unsigned char   *prop_return2 = NULL;
	Window           win_id;
	
	win_id = *(long *)prop_return;
	
	xitk_install_x_error_handler();
	
	status = XGetWindowProperty(display, win_id, atom, 0,
				    1, False, XA_CARDINAL,
				    &type_return, &format_return, &nitems_return,
				    &bytes_after_return, &prop_return2);
	
	xitk_uninstall_x_error_handler();
	
	if((status == Success) && (type_return != None) && (type_return == XA_CARDINAL)) {
	  
	  if((format_return == 32) && (nitems_return == 1) 
	     && (bytes_after_return == 0) && (win_id == *(long *)prop_return2)) {
	    type |= WM_TYPE_GNOME_COMP;
	  }
	}

	if(prop_return2)
	  XFree(prop_return2);

	if((wm_name = get_wm_name(display, win_id, "_NET_WM_NAME")) == NULL) {
	  /*
	   * Enlightenment is Gnome compliant, but don't set 
	   * the _NET_WM_NAME atom property 
	   */
	  wm_name = get_wm_name(display, (XDefaultRootWindow(display)), "_WIN_WM_NAME");
	}
      }
      
      if(prop_return)
	XFree(prop_return);
    }
  }
  
  /* Check for Extended Window Manager Hints (EWMH) Compliant */
  if(((atom = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", True)) != None) && 
     (XInternAtom(display, "_NET_WORKAREA", True) != None)) {
    unsigned char   *prop_return = NULL;
    unsigned long    nitems_return;
    unsigned long    bytes_after_return;
    Atom             type_return;
    int              format_return;
    Status           status;
    
    if((XGetWindowProperty(display, (XDefaultRootWindow(display)), atom, 0, 1, False, XA_WINDOW,
			   &type_return, &format_return, &nitems_return,
			   &bytes_after_return, &prop_return)) == Success) {
      
      if((type_return != None) && (type_return == XA_WINDOW) && 
	 (format_return == 32) && (nitems_return == 1) && (bytes_after_return == 0)) {
	unsigned char   *prop_return2 = NULL;
	Window           win_id;
	
	win_id = *(long *)prop_return;
	
	xitk_install_x_error_handler();
	
	status = XGetWindowProperty(display, win_id, atom, 0,
				    1, False, XA_WINDOW,
				    &type_return, &format_return, &nitems_return,
				    &bytes_after_return, &prop_return2);
	
	xitk_uninstall_x_error_handler();
	
	
	if((status == Success) && (type_return != None) && (type_return == XA_WINDOW) &&
	   (format_return == 32) && (nitems_return == 1) && (bytes_after_return == 0)) {
	  
	  if(win_id == *(long *)prop_return) {
	    if (wm_name) free(wm_name);
	    wm_name = get_wm_name(display, win_id, "_NET_WM_NAME");
	    type |= WM_TYPE_EWMH_COMP;
	  }
	}
	if(prop_return2)
	  XFree(prop_return2);
      }
      if(prop_return)
	XFree(prop_return);
    }
  }

  if(type & WM_TYPE_EWMH_COMP) {
    XA_WIN_LAYER               = XInternAtom(display, "_NET_WM_STATE", False);
    XA_STAYS_ON_TOP            = XInternAtom(display, "_NET_WM_STATE_STAYS_ON_TOP", False);
    XA_NET_WM_STATE            = XInternAtom(display, "_NET_WM_STATE", False);
    XA_NET_WM_STATE_ABOVE      = XInternAtom(display, "_NET_WM_STATE_ABOVE", False);
    XA_NET_WM_STATE_FULLSCREEN = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False);

    XA_WM_WINDOW_TYPE          = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
    XA_WM_WINDOW_TYPE_DESKTOP  = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
    XA_WM_WINDOW_TYPE_DOCK     = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", False);
    XA_WM_WINDOW_TYPE_TOOLBAR  = XInternAtom(display, "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
    XA_WM_WINDOW_TYPE_MENU     = XInternAtom(display, "_NET_WM_WINDOW_TYPE_MENU", False);
    XA_WM_WINDOW_TYPE_UTILITY  = XInternAtom(display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
    XA_WM_WINDOW_TYPE_SPLASH   = XInternAtom(display, "_NET_WM_WINDOW_TYPE_SPLASH", False);
    XA_WM_WINDOW_TYPE_DIALOG   = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
    XA_WM_WINDOW_TYPE_NORMAL   = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
  }
  
  switch(type & WM_TYPE_COMP_MASK) {
  case WM_TYPE_KWIN:
    if(XA_NET_WM_STATE == None)
      XA_NET_WM_STATE    = XInternAtom(display, "_NET_WM_STATE", False);
    if(XA_STAYS_ON_TOP == None)
      XA_STAYS_ON_TOP = XInternAtom(display, "_NET_WM_STATE_STAYS_ON_TOP", False);
    break;

  case WM_TYPE_MOTIF:
  case WM_TYPE_LARSWM:
    break;

  case WM_TYPE_UNKNOWN:
  case WM_TYPE_E:
  case WM_TYPE_ICE:
  case WM_TYPE_WINDOWMAKER:
  case WM_TYPE_XFCE:
  case WM_TYPE_SAWFISH:
  case WM_TYPE_METACITY: /* Untested */
  case WM_TYPE_AFTERSTEP:
  case WM_TYPE_BLACKBOX:
    XA_WIN_LAYER = XInternAtom(display, "_WIN_LAYER", False);
    break;
  }

  XUnlockDisplay(display);
  
  if(gXitk->verbosity) {
    printf("[ WM type: ");
    
    if(type & WM_TYPE_GNOME_COMP)
      printf("(GnomeCompliant) ");
    if(type & WM_TYPE_EWMH_COMP)
      printf("(EWMH) ");
    
    switch(type & WM_TYPE_COMP_MASK) {
    case WM_TYPE_UNKNOWN:
      printf("Unknown");
      break;
    case WM_TYPE_KWIN:
      printf("KWIN");
      break;
    case WM_TYPE_E:
      printf("Enlightenment");
      break;
    case WM_TYPE_ICE:
      printf("Ice");
      break;
    case WM_TYPE_WINDOWMAKER:
      printf("WindowMaker");
      break;
    case WM_TYPE_MOTIF:
      printf("Motif(like?)");
      break;
    case WM_TYPE_XFCE:
      printf("XFce");
      break;
    case WM_TYPE_SAWFISH:
      printf("Sawfish");
      break;
    case WM_TYPE_METACITY:
      printf("Metacity");
      break;
    case WM_TYPE_AFTERSTEP:
      printf("Afterstep");
      break;
    case WM_TYPE_BLACKBOX:
      printf("Blackbox");
      break;
    case WM_TYPE_LARSWM:
      printf("LarsWM");
      break;
    case WM_TYPE_DTWM:
      printf("dtwm");
      break;
    }
    
    if(wm_name)
      printf(" {%s}", wm_name);
    
    printf(" ]-\n");
  }
  
  if(wm_name)
    free(wm_name);
  
  return type;
}
uint32_t xitk_get_wm_type(void) {
  return gXitk->wm_type;
}

int xitk_get_layer_level(void) {
  int level = 10;
  
  if((gXitk->wm_type & WM_TYPE_GNOME_COMP) || (gXitk->wm_type & WM_TYPE_EWMH_COMP))
    level = 10;
  
  switch(gXitk->wm_type & WM_TYPE_COMP_MASK) {
  case WM_TYPE_UNKNOWN:
  case WM_TYPE_KWIN:
  case WM_TYPE_SAWFISH:
  case WM_TYPE_METACITY: /* Untested */
  case WM_TYPE_ICE:
  case WM_TYPE_AFTERSTEP:
  case WM_TYPE_BLACKBOX:
  case WM_TYPE_WINDOWMAKER:
    level = 10; /* Wrong, but we need to provide a default value */
    break;
  case WM_TYPE_XFCE:
    level = 8;
    break;
  case WM_TYPE_E:
    level = 6;
    break;
  case WM_TYPE_MOTIF:
  case WM_TYPE_LARSWM:
    level = 0;
    break;
  }
  return level;     
}

void xitk_set_layer_above(Window window) {

  if((gXitk->wm_type & WM_TYPE_GNOME_COMP) && !(gXitk->wm_type & WM_TYPE_EWMH_COMP)) {
    long propvalue[1];
    
    propvalue[0] = xitk_get_layer_level();

    XLockDisplay(gXitk->display);
    XChangeProperty(gXitk->display, window, XA_WIN_LAYER,
		    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)propvalue,
		    1);
    XUnlockDisplay(gXitk->display);
    return;
  }
  

  if(gXitk->wm_type & WM_TYPE_EWMH_COMP) {
    if(gXitk->wm_type & WM_TYPE_KWIN) {
      XEvent xev;
      
      XLockDisplay(gXitk->display);
      xev.xclient.type         = ClientMessage;
      xev.xclient.display      = gXitk->display;
      xev.xclient.window       = window;
      xev.xclient.message_type = XA_NET_WM_STATE;
      xev.xclient.format       = 32;
      xev.xclient.data.l[0]    = 1;
      xev.xclient.data.l[1]    = XA_STAYS_ON_TOP;
      xev.xclient.data.l[2]    = 0l;
      xev.xclient.data.l[3]    = 0l;
      xev.xclient.data.l[4]    = 0l;
      
      XSendEvent(gXitk->display, DefaultRootWindow(gXitk->display), True, SubstructureRedirectMask, &xev);
      XUnlockDisplay(gXitk->display);
    }
    else {
      XEvent xev;
      
      XLockDisplay(gXitk->display);
      
      xev.xclient.type         = ClientMessage;
      xev.xclient.serial       = 0;
      xev.xclient.send_event   = True;
      xev.xclient.display      = gXitk->display;
      xev.xclient.window       = window;
      xev.xclient.message_type = XA_NET_WM_STATE;
      xev.xclient.format       = 32;
      xev.xclient.data.l[0]    = (long) 1;
      xev.xclient.data.l[1]    = (long) XA_NET_WM_STATE_ABOVE;
      xev.xclient.data.l[2]    = (long) None;
      
      XSendEvent(gXitk->display, DefaultRootWindow(gXitk->display), 
		 False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*) &xev);
      
      XUnlockDisplay(gXitk->display);
    }
    
    return;
  }
  
  switch(gXitk->wm_type & WM_TYPE_COMP_MASK) {
  case WM_TYPE_MOTIF:
  case WM_TYPE_LARSWM:
    return;
    break;
    
  case WM_TYPE_KWIN:
    XLockDisplay(gXitk->display);
    XChangeProperty(gXitk->display, window, XA_WIN_LAYER,
		    XA_ATOM, 32, PropModeReplace, (unsigned char *)&XA_STAYS_ON_TOP, 1);
    XUnlockDisplay(gXitk->display);
    break;
    
  case WM_TYPE_UNKNOWN:
  case WM_TYPE_WINDOWMAKER:
  case WM_TYPE_ICE:
  case WM_TYPE_E:
  case WM_TYPE_XFCE:
  case WM_TYPE_SAWFISH:
  case WM_TYPE_METACITY: /* Untested */
  case WM_TYPE_AFTERSTEP:
  case WM_TYPE_BLACKBOX:
  default:
    {
      long propvalue[1];
      
      propvalue[0] = xitk_get_layer_level();
      
      XLockDisplay(gXitk->display);
      XChangeProperty(gXitk->display, window, XA_WIN_LAYER,
		      XA_CARDINAL, 32, PropModeReplace, (unsigned char *)propvalue,
		      1);
      XUnlockDisplay(gXitk->display);
    }
    break;
  }
}
void xitk_set_window_layer(Window window, int layer) {
  XEvent xev;

  if(((gXitk->wm_type & WM_TYPE_COMP_MASK) == WM_TYPE_KWIN) ||
     ((gXitk->wm_type & WM_TYPE_EWMH_COMP) && !(gXitk->wm_type & WM_TYPE_GNOME_COMP))) {
    return;
  }

  xev.type                 = ClientMessage;
  xev.xclient.type         = ClientMessage;
  xev.xclient.window       = window;
  xev.xclient.message_type = XA_WIN_LAYER;
  xev.xclient.format       = 32;
  xev.xclient.data.l[0]    = (long) layer;
  xev.xclient.data.l[1]    = (long) 0;
  xev.xclient.data.l[2]    = (long) 0;
  xev.xclient.data.l[3]    = (long) 0;

  XLockDisplay(gXitk->display);
  XSendEvent(gXitk->display, RootWindow(gXitk->display, (XDefaultScreen(gXitk->display))), 
	     False, SubstructureNotifyMask, (XEvent*) &xev);
  XUnlockDisplay(gXitk->display);
}

static void _set_ewmh_state(Window window, Atom atom, int enable) {
  XEvent xev;
  
  if((window == None) || (atom == None))
    return;

  xev.xclient.type         = ClientMessage;
  xev.xclient.message_type = XA_NET_WM_STATE;
  xev.xclient.display      = gXitk->display;
  xev.xclient.window       = window;
  xev.xclient.format       = 32;
  xev.xclient.data.l[0]    = (enable == 1) ? 1 : 0;
  xev.xclient.data.l[1]    = atom;
  xev.xclient.data.l[2]    = 0l;
  xev.xclient.data.l[3]    = 0l;
  xev.xclient.data.l[4]    = 0l;

  XLockDisplay(gXitk->display);
  XSendEvent(gXitk->display, DefaultRootWindow(gXitk->display), True, SubstructureRedirectMask, &xev);
  XUnlockDisplay(gXitk->display);
}

void xitk_set_ewmh_fullscreen(Window window) {
  
  if(!(gXitk->wm_type & WM_TYPE_EWMH_COMP) || (window == None))
    return;
  
  _set_ewmh_state(window, XA_NET_WM_STATE_FULLSCREEN, 1);
  _set_ewmh_state(window, XA_STAYS_ON_TOP, 1);
}

void xitk_unset_ewmh_fullscreen(Window window) {
  
  if(!(gXitk->wm_type & WM_TYPE_EWMH_COMP) || (window == None))
    return;
  
  _set_ewmh_state(window, XA_NET_WM_STATE_FULLSCREEN, 0);
  _set_ewmh_state(window, XA_STAYS_ON_TOP, 0);
}

static void _set_wm_window_type(Window window, xitk_wm_window_type_t type, int value) {
  if(window && (gXitk->wm_type & WM_TYPE_EWMH_COMP)) {
    Atom *atom = NULL;
    
    switch(type) {
    case WINDOW_TYPE_DESKTOP:
      atom = &XA_WM_WINDOW_TYPE_DESKTOP;
      break;
    case WINDOW_TYPE_DOCK:
      atom = &XA_WM_WINDOW_TYPE_DOCK;
      break;
    case WINDOW_TYPE_TOOLBAR:
      atom = &XA_WM_WINDOW_TYPE_TOOLBAR;
      break;
    case WINDOW_TYPE_MENU:
      atom = &XA_WM_WINDOW_TYPE_MENU;
      break;
    case WINDOW_TYPE_UTILITY:
      atom = &XA_WM_WINDOW_TYPE_UTILITY;
      break;
    case WINDOW_TYPE_SPLASH:
      atom = &XA_WM_WINDOW_TYPE_SPLASH;
      break;
    case WINDOW_TYPE_DIALOG:
      atom = &XA_WM_WINDOW_TYPE_DIALOG;
      break;
    case WINDOW_TYPE_NORMAL:
      atom = &XA_WM_WINDOW_TYPE_NORMAL;
      break;
    }
    
    if(atom) {
      XEvent xev;

      xev.xclient.type         = ClientMessage;
      xev.xclient.display      = gXitk->display;
      xev.xclient.window       = window;
      xev.xclient.message_type = XA_WIN_LAYER;
      xev.xclient.format       = 32;
      xev.xclient.data.l[0]    = *atom;
      xev.xclient.data.l[1]    = 0l;
      xev.xclient.data.l[2]    = 0l;
      xev.xclient.data.l[3]    = 0l;
      xev.xclient.data.l[4]    = 0l;
  
      XLOCK(gXitk->display);
      XSendEvent(gXitk->display, DefaultRootWindow(gXitk->display), False, SubstructureNotifyMask, &xev);

      XRaiseWindow(gXitk->display, window);
      XUNLOCK(gXitk->display);
    }
  }
}

void xitk_unset_wm_window_type(Window window, xitk_wm_window_type_t type) {
  _set_wm_window_type(window, type, 0);
}

void xitk_set_wm_window_type(Window window, xitk_wm_window_type_t type) {
  _set_wm_window_type(window, type, 1);
}

/*
 * Create a new widget_list, store the pointer in private
 * list of xitk_widget_list_t, then return the widget_list pointer.
 */
xitk_widget_list_t *xitk_widget_list_new (void) {
  xitk_widget_list_t *l;

  l = (xitk_widget_list_t *) xitk_xmalloc(sizeof(xitk_widget_list_t));

  l->widget_focused     = NULL;
  l->widget_under_mouse = NULL;
  l->widget_pressed     = NULL;

  MUTLOCK();

  xitk_list_append_content(gXitk->list, l);

  MUTUNLOCK();

  return l;
}

/*
 * Change the window for the xevent_handler previously initialized
 * at widget_register_event_handler() time. It also remade it
 * DND aware, only if DND stuff was initialized at register time too.
 */
void xitk_change_window_for_event_handler (xitk_register_key_t key, Window window) {
  __gfx_t  *fx;
  
  MUTLOCK();
      
  fx = (__gfx_t *) xitk_list_first_content(gXitk->gfx);
  
  while(fx) {

    FXLOCK(fx);
    if(fx->key == key) {

      fx->window = window;

      if(fx->xdnd && (window != None)) {
	if(!xitk_make_window_dnd_aware(fx->xdnd, window))
	  xitk_unset_dnd_callback(fx->xdnd);
      }
      
      FXUNLOCK(fx);
      MUTUNLOCK();
      return;
    }
    
    FXUNLOCK(fx);
    fx = (__gfx_t *) xitk_list_next_content(gXitk->gfx);
  }

  MUTUNLOCK();
}

/*
 * Register a callback function called when a signal happen.
 */
void xitk_register_signal_handler(xitk_signal_callback_t sigcb, void *user_data) {
  if(sigcb) {
    gXitk->sig_callback = sigcb;
    gXitk->sig_data     = user_data;
  }
}

/*
 * Register a window, with his own event handler, callback
 * for DND events, and widget list.
 */
xitk_register_key_t xitk_register_event_handler(char *name, Window window,
						widget_event_callback_t cb,
						widget_newpos_callback_t pos_cb,
						xitk_dnd_callback_t dnd_cb,
						xitk_widget_list_t *wl, void *user_data) {
  __gfx_t   *fx;
  
  //  printf("%s()\n", __FUNCTION__);

  fx = (__gfx_t *) xitk_xmalloc(sizeof(__gfx_t));

  fx->name = name ? strdup(name) : strdup("NO_SET");

  fx->window    = window;
  fx->new_pos.x = 0;
  fx->new_pos.y = 0;
  fx->width     = 0;
  fx->height    = 0;
  fx->user_data = user_data;
  pthread_mutex_init(&fx->mutex, NULL);
  
  if(window != None) {
    XWindowAttributes wattr;
    Status            err;
    
    XLOCK(gXitk->display);
    err = XGetWindowAttributes(gXitk->display, fx->window, &wattr);
    XUNLOCK(gXitk->display);
    
    if(err != BadDrawable && err != BadWindow) {
      Window c;
      
      XLOCK(gXitk->display);
      XTranslateCoordinates(gXitk->display, fx->window, wattr.root, 
			    0, 0, &(fx->new_pos.x), &(fx->new_pos.y), &c);
      XUNLOCK(gXitk->display);
      
      /*
	fx->new_pos.x = wattr.x;
	fx->new_pos.y = wattr.y;
      */
      fx->width     = wattr.width;
      fx->height    = wattr.height;
    }
  }

  if(cb)
    fx->xevent_callback = cb;
#if 0
  else {
    XITK_DIE("%s()@%d: Callback should be non NULL\n", __FUNCTION__, __LINE__);
  }
#endif

  if(pos_cb && (window != None))
    fx->newpos_callback = pos_cb;
  else
    fx->newpos_callback = NULL;
  
  if(wl)
    fx->widget_list = wl;
  else
    fx->widget_list = NULL;

  if(dnd_cb && (window != None)) {
    fx->xdnd = (xitk_dnd_t *) xitk_xmalloc(sizeof(xitk_dnd_t));
    
    xitk_init_dnd(gXitk->display, fx->xdnd);
    if(xitk_make_window_dnd_aware(fx->xdnd, fx->window))
      xitk_set_dnd_callback(fx->xdnd, dnd_cb);
  }
  else
    fx->xdnd = NULL;

  fx->key = ++gXitk->key;

  if(fx->window) {

    XLOCK(gXitk->display);
    fx->XA_XITK = XInternAtom(gXitk->display, "_XITK_EVENT", False);
    XChangeProperty (gXitk->display, fx->window, fx->XA_XITK, XA_ATOM,
		     32, PropModeAppend, (unsigned char *)&XITK_VERSION, 1);
    XUNLOCK(gXitk->display);

  }
  else
    fx->XA_XITK = None;

  MUTLOCK();

  xitk_list_append_content(gXitk->gfx, fx);

  MUTUNLOCK();

  return fx->key;
}

static void __fx_destroy(__gfx_t *fx, int locked) {
  __gfx_t *cur;
  
  if(!fx)
    return;
  
  if(!locked)
    MUTLOCK();
  
  cur = (__gfx_t *) xitk_list_get_current(gXitk->gfx);
  
  if(fx->xdnd) {
    xitk_unset_dnd_callback(fx->xdnd);
    free(fx->xdnd);
  }
  
  fx->xevent_callback = NULL;
  fx->newpos_callback = NULL;
  fx->user_data       = NULL;

  free(fx->name);
  
  if(cur) {
    if(cur == fx)
      xitk_list_delete_current(gXitk->gfx); 
    else {
      __gfx_t *cfx;
      
      cfx = (__gfx_t *) xitk_list_first_content(gXitk->gfx);
      while(cfx) {
	if(cfx == fx) {
	  xitk_list_delete_current(gXitk->gfx); 
	  break;
	}
	
	cfx = (__gfx_t *) xitk_list_next_content(gXitk->gfx);
      }
      
      /* set current back */
      cfx = (__gfx_t *) xitk_list_first_content(gXitk->gfx);
      while(cfx) {
	
	if(cfx == cur)
	  break;
	
	cfx = (__gfx_t *) xitk_list_next_content(gXitk->gfx);
      }
    }
  }
  
  FXUNLOCK(fx);
  pthread_mutex_destroy(&fx->mutex);

  free(fx);
  
  if(!locked)
    MUTUNLOCK();
}

/*
 * Remove from the list the window/event_handler
 * specified by the key.
 */
void xitk_unregister_event_handler(xitk_register_key_t *key) {
  __gfx_t  *fx;

  //  printf("%s()\n", __FUNCTION__);

  MUTLOCK();
  
  fx = (__gfx_t *) xitk_list_first_content(gXitk->gfx);
  
  while(fx) {

    if(pthread_mutex_trylock(&fx->mutex)) {
      if(fx->key == *key) {
	*key        = 0; 
	fx->destroy = 1;
	MUTUNLOCK();
	return;
      }
    }
    else {
      if(fx->key == *key) {
	*key = 0; 
	
	__fx_destroy(fx, 1);
	fx = NULL;
	MUTUNLOCK();
	return;
      }
      FXUNLOCK(fx);
    }

    fx = (__gfx_t *) xitk_list_next_content(gXitk->gfx);
  }

  MUTUNLOCK();
}

/*
 * Copy window information matching with key in passed window_info_t struct.
 */
int xitk_get_window_info(xitk_register_key_t key, window_info_t *winf) {
  __gfx_t  *fx;

  MUTLOCK();

  fx = (__gfx_t *) xitk_list_first_content(gXitk->gfx);
    
  while(fx) {
    int  already_locked = 0;
    
    if(pthread_mutex_trylock(&fx->mutex))
      already_locked = 1;

    if((fx->key == key) && (fx->window != None)) {
      Window c;
      
      winf->window = fx->window;
      
      if(fx->name)
	winf->name = strdup(fx->name);
      
      XLOCK(gXitk->display);
      XTranslateCoordinates(gXitk->display, fx->window, DefaultRootWindow(gXitk->display), 
			    0, 0, &(fx->new_pos.x), &(fx->new_pos.y), &c);
      XUNLOCK(gXitk->display);
      
      
      winf->x      = fx->new_pos.x;
      winf->y      = fx->new_pos.y;
      winf->height = fx->height;
      winf->width  = fx->width;
      
      if(!already_locked)
	FXUNLOCK(fx);

      MUTUNLOCK();
      return 1;

    }
    
    if(!already_locked)
      FXUNLOCK(fx);
    
    fx = (__gfx_t *) xitk_list_next_content(gXitk->gfx);
  }

  MUTUNLOCK();
  return 0;
}
/*
 * Here events are handled. All widget are locally
 * handled, then if a event handler callback was passed
 * at register time, it will be called.
 */
void xitk_xevent_notify(XEvent *event) {
  __gfx_t  *fx;
     
  if(!(fx = (__gfx_t *) xitk_list_first_content(gXitk->gfx)))
    return;

  if(event->type == KeyPress || event->type == KeyRelease) {

    /* Filter keys that dont't need to be handled by xine  */
    /* and could be used by our screen saver reset "ping". */
    /* So they will not kill tips and menus.               */

    int i;

    for(i = 0; i < sizeof(gXitk->ignore_keys)/sizeof(gXitk->ignore_keys[0]); ++i)
      if(event->xkey.keycode == gXitk->ignore_keys[i])
	return;
  }
  
  FXLOCK(fx);
  
  if(gXitk->modalw != None) {
    while(fx && (fx->window != gXitk->modalw)) {
      
      if(fx->xevent_callback && (fx->window != None && event->type != KeyRelease))
	fx->xevent_callback(event, fx->user_data);
      
      fx = (__gfx_t *) xitk_list_next_content(gXitk->gfx);
    }
  }
  
  while(fx) {

    if(event->type == KeyRelease)
      gettimeofday(&gXitk->keypress, 0);
    
    if(fx->window != None) {
      
      //printf("event %d\n", event->type);

      if(fx->window == event->xany.window) {
	
	switch(event->type) {

	case MappingNotify:
	  XLOCK(gXitk->display);
	  XRefreshKeyboardMapping((XMappingEvent *) event);
	  XUNLOCK(gXitk->display);
	  break;

	case KeyPress: {
	  XKeyEvent      mykeyevent;
	  KeySym         mykey;
	  char           kbuf[256];
	  int            len;
	  int            modifier;
	  int            handled = 0;
	  xitk_widget_t *w = NULL;
	  
	  mykeyevent = event->xkey;

	  xitk_get_key_modifier(event, &modifier);

	  XLOCK(gXitk->display);
	  len = XLookupString(&mykeyevent, kbuf, sizeof(kbuf), &mykey, NULL);
	  XUNLOCK(gXitk->display);

	  xitk_tips_hide_tips();
	  
	  if(fx->widget_list && fx->widget_list->widget_focused) {
	    w = fx->widget_list->widget_focused;
	  }

	  if(w && (((w->type & WIDGET_TYPE_MASK) == WIDGET_TYPE_INPUTTEXT) &&
		   (mykey != XK_Tab) && (mykey != XK_KP_Tab) && (mykey != XK_ISO_Left_Tab))) {
	    
	    xitk_send_key_event(w, event);
	    
	    if((mykey == XK_Return) || (mykey == XK_KP_Enter) || (mykey == XK_ISO_Enter)) {
	      widget_event_t  event;
	      
	      event.type = WIDGET_EVENT_PAINT;
	      (void) w->event(w, &event, NULL);
	      
	      xitk_set_focus_to_next_widget(fx->widget_list, 0);
	    }

	    FXUNLOCK(fx);
	    return;
	  }
	  
	  /* close menu */
	  if(mykey == XK_Escape) {
	    if(w && ((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_MENU)) {
	      xitk_widget_t *m = xitk_menu_get_menu(w);
	      
	      xitk_menu_destroy(m);
	      FXUNLOCK(fx);
	      return;
	    }
	  }
	  /* set focus to next widget */
	  else if((mykey == XK_Tab) || (mykey == XK_KP_Tab) || (mykey == XK_ISO_Left_Tab)) {
	    if(fx->widget_list) {
	      handled = 1;
	      xitk_set_focus_to_next_widget(fx->widget_list, (modifier & MODIFIER_SHIFT));
	    }
	  }
	  /* simulate click event on space/return/enter key event */
	  else if((mykey == XK_space) || (mykey == XK_Return) || 
		  (mykey == XK_KP_Enter) || (mykey == XK_ISO_Enter)) {
	    if(w && (((w->type & WIDGET_CLICKABLE) && (w->type & WIDGET_KEYABLE))
		     && w->visible && w->enable)) {

	    __menu_sim_click:

	      if(w && (((w->type & WIDGET_TYPE_MASK) == WIDGET_TYPE_BUTTON) ||
		       ((w->type & WIDGET_TYPE_MASK) == WIDGET_TYPE_LABELBUTTON) ||
		       ((w->type & WIDGET_TYPE_MASK) == WIDGET_TYPE_CHECKBOX))) {
		widget_event_t         event;
		widget_event_result_t  result;

		handled = 1;

		event.type           = WIDGET_EVENT_CLICK;
		event.x              = w->x;
		event.y              = w->y;
		event.button_pressed = LBUTTON_DOWN;
		event.button         = Button1;

		(void) w->event(w, &event, &result);

		event.button_pressed = LBUTTON_UP;

		(void) w->event(w, &event, &result);

		//		if(fx->xevent_callback)
		//		  fx->xevent_callback(event, fx->user_data);
	      }
	    }
	  }
	  /* move sliders, handle menu items */
	  else if(((mykey == XK_Left) || (mykey == XK_Right) 
		   || (mykey == XK_Up) || (mykey == XK_Down)
		   || (mykey == XK_Prior) || (mykey == XK_Next)) 
		  && ((modifier & 0xFFFFFFEF) == MODIFIER_NOMOD)) {
	    
	    if(w && ((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_BROWSER)) {
	      xitk_widget_t *b = xitk_browser_get_browser(w);
	      
	      if(b) {
		if(mykey == XK_Up)
		  xitk_browser_step_down(b, NULL);
		else if(mykey == XK_Down)
		  xitk_browser_step_up(b, NULL);
		else if(mykey == XK_Left)
		  xitk_browser_step_left(b, NULL);
		else if(mykey == XK_Right)
		  xitk_browser_step_right(b, NULL);
		else if(mykey == XK_Prior)
		  xitk_browser_page_down(b, NULL);
		else if(mykey == XK_Next)
		  xitk_browser_page_up(b, NULL);

	      }
	    }
	    else if(w && (((w->type & WIDGET_TYPE_MASK) == WIDGET_TYPE_SLIDER) 
			  && (w->type & WIDGET_KEYABLE))) {
	      handled = 1;
	      if((mykey == XK_Left) || (mykey == XK_Down) || (mykey == XK_Next)) {
		xitk_slider_make_backstep(w);
	      }
	      else {
		xitk_slider_make_step(w);
	      }
	      xitk_slider_callback_exec(w);
	    }
	    else if(w && ((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_MENU)) {
	      handled = 1;
	      if(mykey == XK_Left) {
		/* close menu branch */
		xitk_menu_destroy_branch(w);
	      }
	      else if(mykey == XK_Right) {
		/* simulate click event: trigger action of menu item (e.g. open submenu) */
		goto __menu_sim_click; // (goto is bad but simple ...)
	      }
	      else {
		/* next/previous menu item */
		if(fx->widget_list)
		  xitk_set_focus_to_next_widget(fx->widget_list,
						((mykey == XK_Up) || (mykey == XK_Prior)));
	      }
	    }
	  }
	  else if((mykey == XK_0) || (mykey == XK_1) || (mykey == XK_2) || (mykey == XK_3) || 
		  (mykey == XK_4) || (mykey == XK_5) || (mykey == XK_6) || (mykey == XK_7) || 
		  (mykey == XK_8) || (mykey == XK_9) || 
		  (mykey == XK_a) || (mykey == XK_A) || (mykey == XK_b) || (mykey == XK_B) ||
		  (mykey == XK_c) || (mykey == XK_C) || (mykey == XK_d) || (mykey == XK_D) ||
		  (mykey == XK_e) || (mykey == XK_E) || (mykey == XK_f) || (mykey == XK_F) ||
		  (mykey == XK_g) || (mykey == XK_G) || (mykey == XK_h) || (mykey == XK_H) ||
		  (mykey == XK_i) || (mykey == XK_I) || (mykey == XK_j) || (mykey == XK_J) ||
		  (mykey == XK_k) || (mykey == XK_K) || (mykey == XK_l) || (mykey == XK_L) ||
		  (mykey == XK_m) || (mykey == XK_M) || (mykey == XK_n) || (mykey == XK_N) ||
		  (mykey == XK_o) || (mykey == XK_O) || (mykey == XK_p) || (mykey == XK_P) ||
		  (mykey == XK_q) || (mykey == XK_Q) || (mykey == XK_r) || (mykey == XK_R) ||
		  (mykey == XK_s) || (mykey == XK_S) || (mykey == XK_t) || (mykey == XK_T) ||
		  (mykey == XK_u) || (mykey == XK_U) || (mykey == XK_v) || (mykey == XK_V) ||
		  (mykey == XK_w) || (mykey == XK_W) || (mykey == XK_x) || (mykey == XK_X) ||
		  (mykey == XK_y) || (mykey == XK_Y) || (mykey == XK_z) || (mykey == XK_Z)) {

	    
	    if(w && ((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_BROWSER)) {
	      xitk_widget_t *b = xitk_browser_get_browser(w);
	      
	      if(b) {
		handled = 1;
		xitk_browser_warp_jump(b, kbuf, modifier);
	      }

	    }
	  }

	  if(!handled) {

	    if(gXitk->menu && 
	       ((fx->widget_list && 
		 ((!fx->widget_list->widget_focused) || 
		  (!(fx->widget_list->widget_focused->type & WIDGET_GROUP_MENU)))) ||
		(!fx->widget_list)))  {
	      
	      xitk_set_current_menu(NULL);
	    }
	    
	    if((w == NULL) || (w && (((w->type & WIDGET_TYPE_MASK) == WIDGET_TYPE_INPUTTEXT) == 0))) {
	      if(fx->xevent_callback) {
		fx->xevent_callback(event, fx->user_data);
	      }
	    }
	    
	    if(w && ((w->type & WIDGET_TYPE_MASK) == WIDGET_TYPE_INPUTTEXT) && 
	       ((mykey == XK_Return) || (mykey == XK_KP_Enter) || (mykey == XK_ISO_Enter))) {
	      widget_event_t  event;
	      
	      event.type = WIDGET_EVENT_PAINT;
	      (void) w->event(w, &event, NULL);
	      
	      xitk_set_focus_to_next_widget(fx->widget_list, 0);
	      
	    }
	  }
	  
	  if(fx->destroy) {
	    __fx_destroy(fx, 0);
	    fx = NULL;
	  }
	  else
	    FXUNLOCK(fx);

	  return;
	}
	break;

	case Expose:
	  if (fx->widget_list) {
	    XEvent xev = *event;

	    XLOCK(gXitk->display);
	    while(XCheckTypedWindowEvent(gXitk->display, fx->window, 
					 Expose, &xev) == True);
	    XUNLOCK(gXitk->display);

	    if(xev.xexpose.count == 0)
	      xitk_paint_widget_list(fx->widget_list);
	  }
	  break;
	  
	case MotionNotify: {
	  XWindowAttributes wattr;
	  Status            err;
	  
	  XLOCK(gXitk->display);
	  while(XCheckMaskEvent(gXitk->display, ButtonMotionMask, event) == True);
	  XUNLOCK(gXitk->display);

	  fx->old_event = event;
	  if(fx->move.enabled) {

	    if(fx->widget_list->widget_focused && 
	       (fx->widget_list->widget_focused->type & WIDGET_GROUP_MENU)) {
	      xitk_widget_t *menu = xitk_menu_get_menu(fx->widget_list->widget_focused);

	      if(xitk_menu_show_sub_branchs(menu))
		xitk_menu_destroy_sub_branchs(menu);

	    }

	    fx->old_pos.x = fx->new_pos.x;
	    fx->old_pos.y = fx->new_pos.y;

	    fx->new_pos.x = (event->xmotion.x_root) 
	      + (event->xmotion.x_root - fx->old_event->xmotion.x_root) 
	      - fx->move.offset_x;
	    fx->new_pos.y = (event->xmotion.y_root) 
	      + (event->xmotion.y_root - fx->old_event->xmotion.y_root) 
	      - fx->move.offset_y;
	    
	    XLOCK(gXitk->display);

	    XMoveWindow(gXitk->display, fx->window,
			fx->new_pos.x, fx->new_pos.y);
	    err = XGetWindowAttributes(gXitk->display, fx->window, &wattr);

	    XUNLOCK(gXitk->display);

	  }
	  else {
	    if(fx->widget_list) {
	      xitk_motion_notify_widget_list (fx->widget_list,
					      event->xmotion.x, 
					      event->xmotion.y, event->xmotion.state);
	    }
	  }
	}
	  break;
	  
	case EnterNotify:
	  if(fx->widget_list)
	    if(event->xcrossing.mode == NotifyNormal) /* Ptr. moved rel. to win., not (un)grab */
	      xitk_motion_notify_widget_list (fx->widget_list,
					      event->xcrossing.x,
					      event->xcrossing.y, event->xcrossing.state);
	  break;
	  
	case LeaveNotify:
	  if(fx->widget_list)
	    if(event->xcrossing.mode == NotifyNormal) /* Ptr. moved rel. to win., not (un)grab */
	      xitk_motion_notify_widget_list (fx->widget_list,
					      -1,
					      -1, event->xcrossing.state);
	  break;
	  
	case ButtonPress: {
	  XWindowAttributes   wattr;
	  Status              status;
	  Window              focused_window;
	  int                 revert;
	  
	  XLOCK(gXitk->display);
	  status = XGetWindowAttributes(gXitk->display, fx->window, &wattr);
	  XGetInputFocus(gXitk->display, &focused_window, &revert);
	  
	  /* 
	   * Give focus(and raise) to a window after a click, and 
	   * only if window isn't the current focused one.
	   */
	  if((status != BadDrawable) && (status != BadWindow) 
	     && (wattr.map_state == IsViewable)/* && (focused_window != fx->window)*/) {
	    XRaiseWindow(gXitk->display, fx->window);
	    XSetInputFocus(gXitk->display, fx->window, RevertToParent, CurrentTime);
	  }
	  XUNLOCK(gXitk->display);
	  
	  if(gXitk->menu && 
	     ((fx->widget_list && 
	       ((!fx->widget_list->widget_focused) || 
		(!(fx->widget_list->widget_focused->type & WIDGET_GROUP_MENU)))) ||
	      (!fx->widget_list)))  {

	    xitk_set_current_menu(NULL);
	  }
	  
	  if(fx->widget_list) {

	    fx->move.enabled = !xitk_click_notify_widget_list (fx->widget_list, 
							       event->xbutton.x, 
							       event->xbutton.y, 
							       event->xbutton.button, 0);
	    if(event->xbutton.button != Button1) {
	      xitk_widget_t *w = fx->widget_list->widget_focused;

	      fx->move.enabled = 0;
	      
	      if(w && ((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_BROWSER)) {
		xitk_widget_t *b = xitk_browser_get_browser(w);
		
		if(b) {
		  
		  if(event->xbutton.button == Button4) {
		    xitk_browser_step_down(b, NULL);
		  }
		  else if(event->xbutton.button == Button5) {
		    xitk_browser_step_up(b, NULL);
		  }
		  
		}
	      }
	    }

	    if(fx->move.enabled) {
	      XWindowAttributes wattr;
	      Status            err;

	      XLOCK(gXitk->display);
	      err = XGetWindowAttributes(gXitk->display, fx->window, &wattr);
	      XUNLOCK(gXitk->display);

	      if(err != BadDrawable && err != BadWindow) {
		
		fx->old_pos.x = event->xmotion.x_root - event->xbutton.x;
		fx->old_pos.y = event->xmotion.y_root - event->xbutton.y;

	      }
	      
	      fx->move.offset_x = event->xbutton.x;
	      fx->move.offset_y = event->xbutton.y;

	    }
	  }
	}
	break;
	  
	case ButtonRelease:
	  
	  if(fx->move.enabled) {

	    fx->move.enabled = 0;
	    /* Inform application about window movement. */

	    if(fx->newpos_callback)
	      fx->newpos_callback(fx->new_pos.x, fx->new_pos.y, 
				  fx->width, fx->height);
	  }
	  else {
	    if(fx->widget_list) {
	      xitk_click_notify_widget_list (fx->widget_list, 
					     event->xbutton.x, event->xbutton.y,
					     event->xbutton.button, 1);
	    }
	  }
	  break;
	  
	case ConfigureNotify: {
	  XWindowAttributes wattr;
	  Status            err;

	  if(fx->widget_list && fx->widget_list->l) {
	    xitk_widget_t *w = (xitk_widget_t *) xitk_list_first_content(fx->widget_list->l);
	    while (w) {
	      if(((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_COMBO) &&
		 (w->type & WIDGET_GROUP_WIDGET)) {
		xitk_combo_update_pos(w);
	      }
	      w = (xitk_widget_t *) xitk_list_next_content (fx->widget_list->l);
	    }
	  }

	  XLOCK(gXitk->display);
	  err = XGetWindowAttributes(gXitk->display, fx->window, &wattr);
	  XUNLOCK(gXitk->display);

	  if(err != BadDrawable && err != BadWindow) {
	    fx->width = wattr.width;
	    fx->height = wattr.height;
	  }
	  /* Inform application about window movement. */
	  if(fx->newpos_callback)
	    fx->newpos_callback(event->xconfigure.x,
				event->xconfigure.y,
				fx->width, fx->height);
	}
	break;

	case SelectionNotify:
	case ClientMessage:
	  if(fx->xdnd)
	    xitk_process_client_dnd_message(fx->xdnd, event);
	  break;
	}
	
	if(fx->xevent_callback) {
	  fx->xevent_callback(event, fx->user_data);
	}
      }
    }
    else {
      
      /* Don't forward event to all of windows */
      if(fx->xevent_callback && (fx->window != None && event->type != KeyRelease))
	fx->xevent_callback(event, fx->user_data);
      
    }
    
    if(fx->destroy) {
      __fx_destroy(fx, 0);
      fx = NULL;
    }
    else
      FXUNLOCK(fx);
    
#warning FIXME
    if(gXitk->modalw != None) {
      fx = (__gfx_t *) xitk_list_next_content(gXitk->gfx);

      /* Flush remain fxs */
      while(fx && (fx->window != gXitk->modalw)) {
	FXLOCK(fx);
	
	if(fx->xevent_callback && (fx->window != None && event->type != KeyRelease))
	  fx->xevent_callback(event, fx->user_data);
	
	if(fx->destroy) {
	  __fx_destroy(fx, 0);
	  fx = NULL;
	}
	else
	  FXUNLOCK(fx);
	
	fx = (__gfx_t *) xitk_list_next_content(gXitk->gfx);
      }
      return;
    }

    if((fx = (__gfx_t *) xitk_list_next_content(gXitk->gfx)))
      FXLOCK(fx);
  }
}

/*
 * Initiatization of widget internals.
 */
void xitk_init(Display *display, XColor black, int verbosity) {
  char buffer[256];
  
  xitk_pid = getppid();

#ifdef ENABLE_NLS
  bindtextdomain("xitk", XITK_LOCALE);
#endif  

  gXitk = (__xitk_t *) xitk_xmalloc(sizeof(__xitk_t));

  gXitk->black           = black;
  gXitk->display_width   = DisplayWidth(display, DefaultScreen(display));
  gXitk->display_height  = DisplayHeight(display, DefaultScreen(display));
  gXitk->verbosity       = verbosity;
  gXitk->list            = xitk_list_new();
  gXitk->gfx             = xitk_list_new();
  gXitk->display         = display;
  gXitk->key             = 0;
  gXitk->sig_callback    = NULL;
  gXitk->sig_data        = NULL;
  gXitk->config          = xitk_config_init();
  gXitk->use_xshm        = (xitk_config_get_shm_feature(gXitk->config)) ? (xitk_check_xshm(display)) : 0;
  xitk_x_error           = 0;
  gXitk->x_error_handler = NULL;
  gXitk->modalw          = None;
  gXitk->ignore_keys[0]  = XKeysymToKeycode(display, XK_Shift_L);
  gXitk->ignore_keys[1]  = XKeysymToKeycode(display, XK_Control_L);
  gXitk->tips_timeout    = TIPS_TIMEOUT;
  XGetInputFocus(display, &(gXitk->parent.window), &(gXitk->parent.focus));

  memset(&gXitk->keypress, 0, sizeof(gXitk->keypress));

  pthread_mutex_init (&gXitk->mutex, NULL);
  
  snprintf(buffer, sizeof(buffer), "-[ xiTK version %d.%d.%d ", XITK_MAJOR_VERSION, XITK_MINOR_VERSION, XITK_SUB_VERSION);
  
  /* Check if SHM is working */
#ifdef HAVE_SHM
  if(gXitk->use_xshm) {
    XImage             *xim;
    XShmSegmentInfo     shminfo;
    
    xim = XShmCreateImage(display, 
			  (DefaultVisual(display, (DefaultScreen(display)))), 
			  (DefaultDepth(display, (DefaultScreen(display)))),
			  ZPixmap, NULL, &shminfo, 10, 10);
    if(!xim)
      gXitk->use_xshm = 0;
    else {
      shminfo.shmid = shmget(IPC_PRIVATE, xim->bytes_per_line * xim->height, IPC_CREAT | 0777);
      if(shminfo.shmid < 0) {
	XDestroyImage(xim);
	gXitk->use_xshm = 0;
      }
      else {
	shminfo.shmaddr = xim->data =  shmat(shminfo.shmid, 0, 0);
	if(shminfo.shmaddr == (char *) -1) {
	  XDestroyImage(xim);
	  gXitk->use_xshm = 0;
	}
	else {
	  shminfo.readOnly = False;
	  
	  xitk_x_error = 0;
	  xitk_install_x_error_handler();
	  
	  XShmAttach(display, &shminfo);
	  XSync(display, False);
	  if(xitk_x_error)
	    gXitk->use_xshm = 0;
	  else {
	    XShmDetach(display, &shminfo);
	    sprintf(buffer, "%s%s", buffer, "[XShm]");
	  }
	  
	  XDestroyImage(xim);
	  shmdt(shminfo.shmaddr);

	  xitk_uninstall_x_error_handler();
	  xitk_x_error = 0;
	}
	shmctl(shminfo.shmid, IPC_RMID, 0);
      }
    }
  }
#endif

#ifdef WITH_XFT
  sprintf(buffer, "%s%s", buffer, "[XFT]");
#elif defined(WITH_XMB)
  sprintf(buffer, "%s%s", buffer, "[XMB]");
#endif
  
  sprintf(buffer, "%s%s", buffer, " ]-");

  if(verbosity)
    printf("%s", buffer);

  gXitk->wm_type = xitk_check_wm(display);
  
  /* init font caching */
  xitk_font_cache_init();
  
  xitk_cursors_init(display);
  xitk_tips_init(display);
}

/*
 * Start widget event handling.
 * It will block till widget_stop() call
 */
void xitk_run(xitk_startup_callback_t cb, void *data) {
  XEvent            myevent;
  struct sigaction  action;
  fd_set            r;
  Bool              got_event;
  __gfx_t          *fx;
  struct timeval    tv;
  int               xconnection;

  action.sa_handler = xitk_signal_handler;
  sigemptyset(&(action.sa_mask));
  action.sa_flags = 0;
  if(sigaction(SIGHUP, &action, NULL) != 0) {
    XITK_WARNING("sigaction(SIGHUP) failed: %s\n", strerror(errno));
  }
  action.sa_handler = xitk_signal_handler;
  sigemptyset(&(action.sa_mask));
  action.sa_flags = 0;
  if(sigaction(SIGUSR1, &action, NULL) != 0) {
    XITK_WARNING("sigaction(SIGUSR1) failed: %s\n", strerror(errno));
  }
  action.sa_handler = xitk_signal_handler;
  sigemptyset(&(action.sa_mask));
  action.sa_flags = 0;
  if(sigaction(SIGUSR2, &action, NULL) != 0) {
    XITK_WARNING("sigaction(SIGUSR2) failed: %s\n", strerror(errno));
  }
  action.sa_handler = xitk_signal_handler;
  sigemptyset(&(action.sa_mask));
  action.sa_flags = 0;
  if(sigaction(SIGINT, &action, NULL) != 0) {
    XITK_WARNING("sigaction(SIGINT) failed: %s\n", strerror(errno));
  }
  action.sa_handler = xitk_signal_handler;
  sigemptyset(&(action.sa_mask));
  action.sa_flags = 0;
  if(sigaction(SIGTERM, &action, NULL) != 0) {
    XITK_WARNING("sigaction(SIGTERM) failed: %s\n", strerror(errno));
  }
  action.sa_handler = xitk_signal_handler;
  sigemptyset(&(action.sa_mask));
  action.sa_flags = 0;
  if(sigaction(SIGQUIT, &action, NULL) != 0) {
    XITK_WARNING("sigaction(SIGQUIT) failed: %s\n", strerror(errno));
  }
#ifndef DEBUG
  action.sa_handler = xitk_signal_handler;
  sigemptyset(&(action.sa_mask));
  action.sa_flags = 0;
  if(sigaction(SIGSEGV, &action, NULL) != 0) {
    XITK_WARNING("sigaction(SIGSEGV) failed: %s\n", strerror(errno));
  }
#endif  
  gXitk->running = 1;
  
  XLOCK(gXitk->display);
  XSync(gXitk->display, True); /* Flushing the toilets */
  XUNLOCK(gXitk->display);

  /*
   * Force to repain the widget list if it exist
   */
  MUTLOCK();
  
  fx = (__gfx_t *) xitk_list_first_content(gXitk->gfx);
  
  while(fx) {
    FXLOCK(fx);
   
    if(fx->window != None && fx->widget_list) {
      XEvent xexp;
      
      xexp.xany.type          = Expose;
      xexp.xexpose.type       = Expose;
      xexp.xexpose.send_event = True;
      xexp.xexpose.display    = gXitk->display;
      xexp.xexpose.window     = fx->window;
      xexp.xexpose.count      = 0;
      
      XLOCK(gXitk->display);
      if(!XSendEvent(gXitk->display, fx->window, False, ExposureMask, &xexp)) {
	XITK_WARNING("XSendEvent(display, 0x%x ...) failed.\n", (unsigned int) fx->window);
      }
      XUNLOCK(gXitk->display);
    }

    FXUNLOCK(fx);
    fx = (__gfx_t *) xitk_list_next_content(gXitk->gfx);
  }

  MUTUNLOCK();

  /* We're ready to handle anything */
  if(cb)
    cb(data);

  XLOCK(gXitk->display);
  xconnection = ConnectionNumber(gXitk->display);
  XUNLOCK(gXitk->display);
  
  /*
   * Now, wait for a new xevent
   */
  while(gXitk->running) {
    
    FD_ZERO(&r);
    FD_SET(xconnection, &r);

    tv.tv_sec  = 0;
    tv.tv_usec = 33000;

    select(xconnection + 1, &r, 0, 0, &tv);
    
    XLOCK(gXitk->display);
    got_event = (XPending(gXitk->display) != 0);
    if( got_event )
      XNextEvent(gXitk->display, &myevent);
    XUNLOCK(gXitk->display);
      
    while(got_event == True) {

      xitk_xevent_notify(&myevent);

      XLOCK(gXitk->display);
      got_event = (XPending(gXitk->display) != 0);
      if( got_event )
        XNextEvent(gXitk->display, &myevent);
      XUNLOCK(gXitk->display);
    }

  }

  /* destroy font caching */
  xitk_font_cache_done();
  
  xitk_list_free(gXitk->list);
  xitk_list_free(gXitk->gfx);
  xitk_config_deinit(gXitk->config);
  
  XITK_FREE(gXitk);

}

/*
 * Stop the wait xevent loop
 */
void xitk_stop(void) {
  xitk_tips_deinit();
  xitk_cursors_deinit(gXitk->display);
  gXitk->running = 0;

  if(gXitk->parent.window != None)
    XSetInputFocus(gXitk->display, gXitk->parent.window, gXitk->parent.focus, CurrentTime);
}
 
char *xitk_get_system_font(void) {
  return xitk_config_get_system_font(gXitk->config);
}
char *xitk_get_default_font(void) {
  return xitk_config_get_default_font(gXitk->config);
}
int xitk_get_xmb_enability(void) {
  return xitk_config_get_xmb_enability(gXitk->config);
}
void xitk_set_xmb_enability(int value) {
  xitk_config_set_xmb_enability(gXitk->config, value);
}
int xitk_get_black_color(void) {
  return xitk_config_get_black_color(gXitk->config);
}
int xitk_get_white_color(void) {
  return xitk_config_get_white_color(gXitk->config);
}
int xitk_get_background_color(void) {
  return xitk_config_get_background_color(gXitk->config);
}
int xitk_get_focus_color(void) {
  return xitk_config_get_focus_color(gXitk->config);
}
int xitk_get_select_color(void) {
  return xitk_config_get_select_color(gXitk->config);
}
unsigned long xitk_get_timer_label_animation(void) {
  return xitk_config_get_timer_label_animation(gXitk->config);
}
unsigned long xitk_get_warning_foreground(void) {
  return xitk_config_get_warning_foreground(gXitk->config);
}
unsigned long xitk_get_warning_background(void) {
  return xitk_config_get_warning_background(gXitk->config);
}
long int xitk_get_timer_dbl_click(void) {
  return xitk_config_get_timer_dbl_click(gXitk->config);
}
int xitk_get_barstyle_feature(void) {
  return xitk_config_get_barstyle_feature(gXitk->config);
}
int xitk_get_checkstyle_feature(void) {
  return xitk_config_get_checkstyle_feature(gXitk->config);
}
int xitk_get_cursors_feature(void) {
  return xitk_config_get_cursors_feature(gXitk->config);
}

int xitk_get_menu_shortcuts_enability(void) {
  return xitk_config_get_menu_shortcuts_enability(gXitk->config);
}

int xitk_get_display_width(void) {
  return gXitk->display_width;
}
int xitk_get_display_height(void) {
  return gXitk->display_height;
}
XColor xitk_get_black_pixel_color(void) {
  return gXitk->black;
}

unsigned long xitk_get_tips_timeout(void) {
  return gXitk->tips_timeout;
}
void xitk_set_tips_timeout(unsigned long timeout) {
  gXitk->tips_timeout = timeout;
}

/*
 * copy src to dest and substitute special chars. dest should have 
 * enought space to store chars.
 */
void xitk_subst_special_chars(char *src, char *dest) {
  char *s, *d;
  
  if((src == NULL) || (dest == NULL)) {
    XITK_WARNING("pass NULL argument(s)\n");
    return;
  }
  
  if(!strlen(src))
    return;

  memset(dest, 0, sizeof(dest));
  s = src;
  d = dest;
  while(*s != '\0') {
    
    switch(*s) {
    case '%':
      if((*(s) == '%') && (*(s + 1) != '%')) {
	char    buffer[5] = { '0', 'x', *(s + 1) , *(s + 2), 0 };
	char   *p         = buffer;
	int     character = strtol(p, &p, 16);
	
	*d = character;
	s += 2;
      }
      else {
	*d++ = '%';
	*d = '%';
      }
      break;
      
    case '~':
      if(*(s + 1) == '/') {
	strcat(d, xitk_get_homedir());
	d += (strlen(xitk_get_homedir()) - 1);
      } else
        *d = *s;
      break;
      
    default:
      *d = *s;
      break;
    }
    s++;
    d++;
  }
  *d = '\0';
}

/*
 *
 */
char *xitk_set_locale(void) {
  char *cur_locale = NULL;
  
#ifdef HAVE_SETLOCALE
  if(setlocale (LC_ALL,"") == NULL) {
    XITK_WARNING("locale not supported by C library\n");
    return NULL;
  }
  
  cur_locale = setlocale(LC_ALL, NULL);
#endif
  
  return cur_locale;
}


/*
 *
 */
long int xitk_get_last_keypressed_time(void) {
  struct timeval tm, tm_diff;
  
  gettimeofday(&tm, NULL);
  timersub(&tm, &gXitk->keypress, &tm_diff);
  return tm_diff.tv_sec;
}

/*
 * Return home directory.
 */
const char *xitk_get_homedir(void) {
#ifdef HAVE_GETPWUID_R
  struct passwd  pwd;
#endif
  struct passwd *pw = NULL;
  static char    homedir[BUFSIZ] = {0,};

  if(homedir[0])
    return homedir;

#ifdef HAVE_GETPWUID_R
  if(getpwuid_r(getuid(), &pwd, homedir, sizeof(homedir), &pw) != 0 || pw == NULL) {
#else
  if((pw = getpwuid(getuid())) == NULL) {
#endif
    char *tmp = getenv("HOME");
    if(tmp) {
      strncpy(homedir, tmp, sizeof(homedir));
      homedir[sizeof(homedir) - 1] = '\0';
    }
  } else {
    char *s = strdup(pw->pw_dir);
    strncpy(homedir, s, sizeof(homedir));
    homedir[sizeof(homedir) - 1] = '\0';
    free(s);
  }

  if(!homedir[0]) {
    XITK_WARNING("Unable to get home directory, set it to /tmp.\n");
    strcpy(homedir, "/tmp");
  }

  return homedir;
}

/*
 * Return 0/1 from char value (valids are 1/0, true/false, 
 * yes/no, on/off. Case isn't checked.
 */
int xitk_get_bool_value(const char *val) {
  static struct {
    const char *str;
    int value;
  } bools[] = {
    { "1",     1 }, { "true",  1 }, { "yes",   1 }, { "on",    1 },
    { "0",     0 }, { "false", 0 }, { "no",    0 }, { "off",   0 },
    { NULL,    0 }
  };
  int i;
  
  ABORT_IF_NULL(val);

  for(i = 0; bools[i].str != NULL; i++) {
    if(!(strcasecmp(bools[i].str, val)))
      return bools[i].value;
  }

  return 0;
}
