view gui/wm/ws.c @ 35412:2de8e26093c4

Don't unconditionally reset AudioChannels after playback. Only do so if no media opened. This will continue displaying the correct audio channel information for the file still being opened after playback.
author ib
date Thu, 29 Nov 2012 11:35:22 +0000
parents 90d181c052e5
children 5cbbd9ca4010
line wrap: on
line source

/*
 * AutoSpace Window System for Linux/Win32 v0.85
 * written by pontscho/fresh!mindworkz
 *
 * This file is part of MPlayer.
 *
 * MPlayer 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.
 *
 * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <stdint.h>

#include "gui/interface.h"
#include "config.h"
#include "libvo/x11_common.h"
#include "libvo/video_out.h"
#include "cpudetect.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libmpcodecs/vf_scale.h"
#include "mp_core.h"
#include "mp_msg.h"
#include "help_mp.h"
#include "mplayer.h"
#include "mpbswap.h"
#include "osdep/timer.h"
#include "ws.h"
#include "wsxdnd.h"

#ifdef CONFIG_XSHAPE
#include <X11/extensions/shape.h>
#endif

#ifdef CONFIG_XF86VM
#include <X11/extensions/xf86vmode.h>
#endif

#include <sys/ipc.h>
#ifdef HAVE_SHM
#include <sys/shm.h>
#endif

#define MOUSEHIDE_DELAY 1000   // in milliseconds

static wsTWindow *mouse_win;
static unsigned int mouse_time;

typedef struct {
    unsigned long flags;
    unsigned long functions;
    unsigned long decorations;
    long input_mode;
    unsigned long status;
} MotifWmHints;

Atom wsMotifHints;

int wsMaxX = 0;                          // Screen width.
int wsMaxY = 0;                          // Screen height.
int wsOrgX = 0;                          // Screen origin x.
int wsOrgY = 0;                          // Screen origin y.

Display *wsDisplay;
int wsScreen;
Window wsRootWin;
XEvent wsEvent;
int wsWindowDepth;
GC wsHGC;
MotifWmHints wsMotifWmHints;
Atom wsTextProperlyAtom = None;
int wsLayer = 0;

int wsDepthOnScreen  = 0;
int wsRedMask        = 0;
int wsGreenMask      = 0;
int wsBlueMask       = 0;
int wsOutMask        = 0;
int wsNonNativeOrder = 0;

int wsTrue = True;

#define wsWLCount 5
wsTWindow *wsWindowList[wsWLCount] = { NULL, NULL, NULL, NULL, NULL };

unsigned long wsKeyTable[512];

int wsUseXShm   = 1;
int wsUseXShape = 1;

static int wsSearch(Window win)
{
    int i;

    for (i = 0; i < wsWLCount; i++)
        if (wsWindowList[i] && wsWindowList[i]->WindowID == win)
            return i;

    return -1;
}

/* --- */

#define PACK_RGB16(r, g, b, pixel) pixel = (b >> 3); \
    pixel <<= 6; \
    pixel  |= (g >> 2); \
    pixel <<= 5; \
    pixel  |= (r >> 3)

#define PACK_RGB15(r, g, b, pixel) pixel = (b >> 3); \
    pixel <<= 5; \
    pixel  |= (g >> 3); \
    pixel <<= 5; \
    pixel  |= (r >> 3)

struct SwsContext *sws_ctx   = NULL;
enum PixelFormat out_pix_fmt = PIX_FMT_NONE;

/* --- */

#define MWM_HINTS_FUNCTIONS     (1L << 0)
#define MWM_HINTS_DECORATIONS   (1L << 1)
#define MWM_HINTS_INPUT_MODE    (1L << 2)
#define MWM_HINTS_STATUS        (1L << 3)

#define MWM_FUNC_ALL            (1L << 0)
#define MWM_FUNC_RESIZE         (1L << 1)
#define MWM_FUNC_MOVE           (1L << 2)
#define MWM_FUNC_MINIMIZE       (1L << 3)
#define MWM_FUNC_MAXIMIZE       (1L << 4)
#define MWM_FUNC_CLOSE          (1L << 5)

#define MWM_DECOR_ALL           (1L << 0)
#define MWM_DECOR_BORDER        (1L << 1)
#define MWM_DECOR_RESIZEH       (1L << 2)
#define MWM_DECOR_TITLE         (1L << 3)
#define MWM_DECOR_MENU          (1L << 4)
#define MWM_DECOR_MINIMIZE      (1L << 5)
#define MWM_DECOR_MAXIMIZE      (1L << 6)

#define MWM_INPUT_MODELESS 0
#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
#define MWM_INPUT_SYSTEM_MODAL 2
#define MWM_INPUT_FULL_APPLICATION_MODAL 3
#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL

#define MWM_TEAROFF_WINDOW      (1L << 0)

void wsWindowDecoration(wsTWindow *win, long d)
{
    wsMotifHints = XInternAtom(wsDisplay, "_MOTIF_WM_HINTS", 0);

    if (wsMotifHints == None)
        return;

    memset(&wsMotifWmHints, 0, sizeof(MotifWmHints));
    wsMotifWmHints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;

    if (d) {
        wsMotifWmHints.functions   = MWM_FUNC_MOVE | MWM_FUNC_CLOSE | MWM_FUNC_MINIMIZE | MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE;
        wsMotifWmHints.decorations = MWM_DECOR_ALL;
    }

    XChangeProperty(wsDisplay, win->WindowID, wsMotifHints, wsMotifHints, 32,
                    PropModeReplace, (unsigned char *)&wsMotifWmHints, 5);
}

// ----------------------------------------------------------------------------------------------
//   Init X Window System.
// ----------------------------------------------------------------------------------------------

static int wsErrorHandler(Display *dpy, XErrorEvent *Event)
{
    char type[128];

    XGetErrorText(dpy, Event->error_code, type, 128);
    fprintf(stderr, "[ws] Error in display.\n");
    fprintf(stderr, "[ws]  Error code: %d ( %s )\n", Event->error_code, type);
    fprintf(stderr, "[ws]  Request code: %d\n", Event->request_code);
    fprintf(stderr, "[ws]  Minor code: %d\n", Event->minor_code);
    fprintf(stderr, "[ws]  Modules: %s\n", current_module ? current_module : "(NULL)");
    return 0;
}

/**
 * @brief Update screen width, screen height and screen origin x and y
 *        from xinerama information.
 *
 *        Set wsOrgX, wsOrgY, wsMaxX and wsMaxY as well as
 *        win->X, win->Y, win->Width and win->Height.
 *
 * @param win pointer to a ws window structure or NULL
 */
static void wsUpdateXineramaInfo(wsTWindow *win)
{
    if (win) {
        vo_dx      = win->X;
        vo_dy      = win->Y;
        vo_dwidth  = win->Width;
        vo_dheight = win->Height;
    }

    vo_screenwidth  = wsMaxX;
    vo_screenheight = wsMaxY;

    update_xinerama_info();

    wsMaxX = vo_screenwidth;
    wsMaxY = vo_screenheight;
    wsOrgX = xinerama_x;
    wsOrgY = xinerama_y;

    if (win) {
        win->X      = wsOrgX;
        win->Y      = wsOrgY;
        win->Width  = wsMaxX;
        win->Height = wsMaxY;
    }
}

void wsXInit(Display *mDisplay)
{
    int eventbase;
    int errorbase;

    // NOTE TO MYSELF: Use global mDisplay, get rid of wsDisplay.
    wsDisplay = mDisplay;

    XSetErrorHandler(wsErrorHandler);

/* enable DND atoms */
    wsXDNDInitialize();

    { /* on remote display XShm will be disabled - LGB */
        char *dispname = DisplayString(wsDisplay);
        int localdisp  = 1;

        if (dispname && *dispname != ':') {
            localdisp = 0;
            wsUseXShm = 0;
        }

        mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] display name: %s => %s display.\n", dispname, localdisp ? "local" : "REMOTE");

        if (!localdisp)
            mp_msg(MSGT_GPLAYER, MSGL_INFO, MSGTR_WS_RemoteDisplay);
    }

#ifdef HAVE_SHM
    if (!XShmQueryExtension(wsDisplay))
#endif
    wsUseXShm = 0;

    if (!wsUseXShm)
        mp_msg(MSGT_GPLAYER, MSGL_INFO, MSGTR_WS_NoXshm);

#ifdef CONFIG_XSHAPE
    if (!XShapeQueryExtension(wsDisplay, &eventbase, &errorbase))
#endif
    wsUseXShape = 0;

    if (!wsUseXShape)
        mp_msg(MSGT_GPLAYER, MSGL_WARN, MSGTR_WS_NoXshape);

    XSynchronize(wsDisplay, True);

    wsScreen  = DefaultScreen(wsDisplay);
    wsRootWin = RootWindow(wsDisplay, wsScreen);
#ifdef CONFIG_XF86VM
    {
        int clock;
        XF86VidModeModeLine modeline;

        XF86VidModeGetModeLine(wsDisplay, wsScreen, &clock, &modeline);
        wsMaxX = modeline.hdisplay;
        wsMaxY = modeline.vdisplay;
    }
#endif
    {
        wsOrgX = wsOrgY = 0;

        if (!wsMaxX)
            wsMaxX = DisplayWidth(wsDisplay, wsScreen);

        if (!wsMaxY)
            wsMaxY = DisplayHeight(wsDisplay, wsScreen);
    }

    wsUpdateXineramaInfo(NULL);

    wsGetDepthOnScreen();

    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] Screen depth: %d\n", wsDepthOnScreen);
    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws]  size: %dx%d\n", wsMaxX, wsMaxY);

#ifdef CONFIG_XINERAMA
    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws]  origin: +%d+%d\n", wsOrgX, wsOrgY);
#endif

    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws]  red mask: 0x%x\n", wsRedMask);
    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws]  green mask: 0x%x\n", wsGreenMask);
    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws]  blue mask: 0x%x\n", wsBlueMask);

#ifdef HAVE_SHM
    if (wsUseXShm) {
        int minor, major, shp;

        XShmQueryVersion(wsDisplay, &major, &minor, &shp);
        mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] XShm version is %d.%d\n", major, minor);
    }
#endif

#ifdef CONFIG_XSHAPE
    if (wsUseXShape) {
        int minor, major;

        XShapeQueryVersion(wsDisplay, &major, &minor);
        mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] XShape version is %d.%d\n", major, minor);
    }
#endif

    wsOutMask = wsGetOutMask();

    switch (wsOutMask) {
    case wsRGB32:
        out_pix_fmt = PIX_FMT_RGB32;
        break;

    case wsBGR32:
        out_pix_fmt = PIX_FMT_BGR32;
        break;

    case wsRGB24:
        out_pix_fmt = PIX_FMT_RGB24;
        break;

    case wsBGR24:
        out_pix_fmt = PIX_FMT_BGR24;
        break;

    case wsRGB16:
        out_pix_fmt = PIX_FMT_RGB565;
        break;

    case wsBGR16:
        out_pix_fmt = PIX_FMT_BGR565;
        break;

    case wsRGB15:
        out_pix_fmt = PIX_FMT_RGB555;
        break;

    case wsBGR15:
        out_pix_fmt = PIX_FMT_BGR555;
        break;
    }
}

/**
 * @brief Calculate and store the x and y position for a window.
 *
 * @param win pointer to a ws window structure
 * @param x x position of the window (real/absolute or mock)
 * @param y y position of the window (real/absolute or mock)
 * @param width width of the area to place the window in
 * @param height height of the area to place the window in
 */
static void wsWindowPosition(wsTWindow *win, int x, int y, int width, int height)
{
    switch (x) {
    case -1:
        win->X = wsOrgX + (wsMaxX - width) / 2;
        break;

    case -2:
        win->X = wsOrgX + wsMaxX - width;
        break;

    default:
        win->X = x;
        break;
    }

    switch (y) {
    case -1:
        win->Y = wsOrgY + (wsMaxY - height) / 2;
        break;

    case -2:
        win->Y = wsOrgY + wsMaxY - height;
        break;

    default:
        win->Y = y;
        break;
    }
}

// ----------------------------------------------------------------------------------------------
//   Create window.
//     X,Y   : window position
//     wX,wY : size of window
//     bW    : border width
//     cV    : visible mouse cursor on window
//     D     : visible frame, title, etc.
//     sR    : screen ratio
// ----------------------------------------------------------------------------------------------

XClassHint wsClassHint;
XTextProperty wsTextProperty;
Window LeaderWindow;

void wsCreateWindow(wsTWindow *win, int X, int Y, int wX, int hY, int bW, int cV, unsigned char D, char *label)
{
    int depth;

    win->Property = D;

    if (D & wsShowFrame)
        win->Decorations = 1;

    wsHGC = DefaultGC(wsDisplay, wsScreen);

    wsWindowPosition(win, X, Y, wX, hY);

    win->Width     = wX;
    win->Height    = hY;
    win->OldX      = win->X;
    win->OldY      = win->Y;
    win->OldWidth  = win->Width;
    win->OldHeight = win->Height;

/* Border size for window. */
    win->BorderWidth = bW;
/* Hide Mouse Cursor */
    win->wsCursor = None;
    win->wsMouseEventType = cV;
    win->wsCursorData[0]  = 0;
    win->wsCursorPixmap   = XCreateBitmapFromData(wsDisplay, wsRootWin, win->wsCursorData, 1, 1);

    if (!(cV & wsShowMouseCursor))
        win->wsCursor = XCreatePixmapCursor(wsDisplay, win->wsCursorPixmap, win->wsCursorPixmap, &win->wsColor, &win->wsColor, 0, 0);

    depth = vo_find_depth_from_visuals(wsDisplay, wsScreen, NULL);

    if (depth < 15) {
        mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_ColorDepthTooLow);
        mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0);
    }

    XMatchVisualInfo(wsDisplay, wsScreen, depth, TrueColor, &win->VisualInfo);

/* --- */
    win->AtomLeaderClient = XInternAtom(wsDisplay, "WM_CLIENT_LEADER", False);
    win->AtomDeleteWindow = XInternAtom(wsDisplay, "WM_DELETE_WINDOW", False);
    win->AtomTakeFocus    = XInternAtom(wsDisplay, "WM_TAKE_FOCUS", False);
    win->AtomRolle         = XInternAtom(wsDisplay, "WM_WINDOW_ROLE", False);
    win->AtomWMSizeHint    = XInternAtom(wsDisplay, "WM_SIZE_HINT", False);
    win->AtomWMNormalHint  = XInternAtom(wsDisplay, "WM_NORMAL_HINT", False);
    win->AtomProtocols     = XInternAtom(wsDisplay, "WM_PROTOCOLS", False);
    win->AtomsProtocols[0] = win->AtomDeleteWindow;
    win->AtomsProtocols[1] = win->AtomTakeFocus;
    win->AtomsProtocols[2] = win->AtomRolle;
/* --- */

    win->WindowAttrib.background_pixel = BlackPixel(wsDisplay, wsScreen);
    win->WindowAttrib.border_pixel     = WhitePixel(wsDisplay, wsScreen);
    win->WindowAttrib.colormap   = XCreateColormap(wsDisplay, wsRootWin, win->VisualInfo.visual, AllocNone);
    win->WindowAttrib.event_mask = StructureNotifyMask | FocusChangeMask |
                                   ExposureMask | PropertyChangeMask |
                                   EnterWindowMask | LeaveWindowMask |
                                   VisibilityChangeMask |
                                   KeyPressMask | KeyReleaseMask;

    if ((cV & wsHandleMouseButton))
        win->WindowAttrib.event_mask |= ButtonPressMask | ButtonReleaseMask;

    if ((cV & wsHandleMouseMove))
        win->WindowAttrib.event_mask |= PointerMotionMask;

    win->WindowAttrib.cursor = win->wsCursor;
    win->WindowAttrib.override_redirect = False;

    if (D & wsOverredirect)
        win->WindowAttrib.override_redirect = True;

    win->WindowMask = CWBackPixel | CWBorderPixel |
                      CWColormap | CWEventMask | CWCursor |
                      CWOverrideRedirect;

    win->WindowID = XCreateWindow(wsDisplay,
                                  (win->Parent != 0 ? win->Parent : wsRootWin),
                                  win->X, win->Y, win->Width, win->Height, win->BorderWidth,
                                  win->VisualInfo.depth,
                                  InputOutput,
                                  win->VisualInfo.visual,
                                  win->WindowMask, &win->WindowAttrib);

    wsClassHint.res_name = "MPlayer";

    wsClassHint.res_class = "MPlayer";
    XSetClassHint(wsDisplay, win->WindowID, &wsClassHint);

    win->SizeHint.flags  = PPosition | PSize | PResizeInc | PWinGravity; // | PBaseSize;
    win->SizeHint.x      = win->X;
    win->SizeHint.y      = win->Y;
    win->SizeHint.width  = win->Width;
    win->SizeHint.height = win->Height;

    if (D & wsMinSize) {
        win->SizeHint.flags     |= PMinSize;
        win->SizeHint.min_width  = win->Width;
        win->SizeHint.min_height = win->Height;
    }

    if (D & wsMaxSize) {
        win->SizeHint.flags     |= PMaxSize;
        win->SizeHint.max_width  = win->Width;
        win->SizeHint.max_height = win->Height;
    }

    win->SizeHint.height_inc  = 1;
    win->SizeHint.width_inc   = 1;
    win->SizeHint.base_width  = win->Width;
    win->SizeHint.base_height = win->Height;
    win->SizeHint.win_gravity = StaticGravity;
    XSetWMNormalHints(wsDisplay, win->WindowID, &win->SizeHint);

    win->WMHints.flags = InputHint | StateHint;
    win->WMHints.input = True;
    win->WMHints.initial_state = NormalState;
    XSetWMHints(wsDisplay, win->WindowID, &win->WMHints);

    wsWindowDecoration(win, win->Decorations);
    XStoreName(wsDisplay, win->WindowID, label);
    XmbSetWMProperties(wsDisplay, win->WindowID, label, label, NULL, 0, NULL, NULL, NULL);

    XSetWMProtocols(wsDisplay, win->WindowID, win->AtomsProtocols, 3);
    XChangeProperty(wsDisplay, win->WindowID,
                    win->AtomLeaderClient,
                    XA_WINDOW, 32, PropModeReplace,
                    (unsigned char *)&LeaderWindow, 1);

    wsTextProperty.value    = label;
    wsTextProperty.encoding = XA_STRING;
    wsTextProperty.format   = 8;
    wsTextProperty.nitems   = strlen(label);
    XSetWMIconName(wsDisplay, win->WindowID, &wsTextProperty);

    win->wGC = XCreateGC(wsDisplay, win->WindowID,
                         GCForeground | GCBackground,
                         &win->wGCV);

    win->Visible = 0;
    win->Focused = 0;
    win->Mapped  = 0;
    win->Rolled  = 0;

    if (D & wsShowWindow)
        XMapWindow(wsDisplay, win->WindowID);

    wsCreateImage(win, win->Width, win->Height);
/* End of creating -------------------------------------------------------------------------- */

    {
        int i;

        for (i = 0; i < wsWLCount; i++)
            if (wsWindowList[i] == NULL)
                break;

        if (i == wsWLCount) {
            mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_TooManyOpenWindows);
            mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0);
        }

        wsWindowList[i] = win;
    }

    XFlush(wsDisplay);
    XSync(wsDisplay, False);

    win->ReDraw       = NULL;
    win->ReSize       = NULL;
    win->Idle         = NULL;
    win->MouseHandler = NULL;
    win->KeyHandler   = NULL;
    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] window is created. ( %s ).\n", label);
}

void wsDestroyWindow(wsTWindow *win)
{
    int l;

    l = wsSearch(win->WindowID);

    if (l != -1)
        wsWindowList[l] = NULL;

    if (win->wsCursor != None) {
        XFreeCursor(wsDisplay, win->wsCursor);
        win->wsCursor = None;
    }

    XFreeGC(wsDisplay, win->wGC);
    XUnmapWindow(wsDisplay, win->WindowID);
    wsDestroyImage(win);
    XDestroyWindow(wsDisplay, win->WindowID);
#if 0
    win->ReDraw       = NULL;
    win->ReSize       = NULL;
    win->Idle         = NULL;
    win->MouseHandler = NULL;
    win->KeyHandler   = NULL;
    win->Visible      = 0;
    win->Focused      = 0;
    win->Mapped       = 0;
    win->Rolled       = 0;
#endif
}

/**
 * @brief Handle automatic hiding of the cursor.
 */
void wsAutohideCursor(void)
{
    if (mouse_win && (GetTimerMS() - mouse_time >= MOUSEHIDE_DELAY)) {
        wsVisibleMouse(mouse_win, wsHideMouseCursor);
        mouse_win = NULL;
    }
}

// ----------------------------------------------------------------------------------------------
//   Handle events.
// ----------------------------------------------------------------------------------------------

Bool wsEvents(Display *display, XEvent *Event)
{
    unsigned long i = 0;
    int l;
    int x, y;
    Window child_window = 0;

    l = wsSearch(Event->xany.window);

    if (l == -1)
        return !wsTrue;

    wsWindowList[l]->State = 0;

    switch (Event->type) {
    case ClientMessage:

        if (Event->xclient.message_type == wsWindowList[l]->AtomProtocols) {
            if ((Atom)Event->xclient.data.l[0] == wsWindowList[l]->AtomDeleteWindow) {
                i = wsWindowClosed;
                goto expose;
            }

            if ((Atom)Event->xclient.data.l[0] == wsWindowList[l]->AtomTakeFocus) {
                i = wsWindowFocusIn;
                wsWindowList[l]->Focused = wsFocused;
                goto expose;
            }

            if ((Atom)Event->xclient.data.l[0] == wsWindowList[l]->AtomRolle) {
                mp_msg(MSGT_GPLAYER, MSGL_V, "[ws] role set.\n");
            }
        } else {
            /* try to process DND events */
            wsXDNDProcessClientMessage(&Event->xclient);
        }

        break;

    case MapNotify:
        i = wsWindowMapped;
        wsWindowList[l]->Mapped = wsMapped;
        goto expose;

    case UnmapNotify:
        i = wsWindowUnmapped;
        wsWindowList[l]->Mapped = wsNone;
        goto expose;

    case FocusIn:

        if (wsWindowList[l]->Focused == wsFocused)
            break;

        i = wsWindowFocusIn;
        wsWindowList[l]->Focused = wsFocused;
        goto expose;

    case FocusOut:

        if (wsWindowList[l]->Focused == wsNone)
            break;

        i = wsWindowFocusOut;
        wsWindowList[l]->Focused = wsNone;
        goto expose;

    case VisibilityNotify:

        switch (Event->xvisibility.state) {
        case VisibilityUnobscured:
            i = wsWindowVisible;
            wsWindowList[l]->Visible = wsVisible;
            goto expose;

        case VisibilityFullyObscured:
            i = wsWindowNotVisible;
            wsWindowList[l]->Visible = wsNotVisible;
            goto expose;

        case VisibilityPartiallyObscured:
            i = wsWindowPartialVisible;
            wsWindowList[l]->Visible = wsPVisible;
            goto expose;
        }

expose:
        wsWindowList[l]->State = i;

        if (wsWindowList[l]->ReDraw)
            wsWindowList[l]->ReDraw();

        break;

    case Expose:
        wsWindowList[l]->State = wsWindowExpose;

        if ((wsWindowList[l]->ReDraw) && (!Event->xexpose.count))
            wsWindowList[l]->ReDraw();

        break;

    case ConfigureNotify:
        XTranslateCoordinates(wsDisplay, wsWindowList[l]->WindowID, wsRootWin, 0, 0, &x, &y, &child_window);

        if ((wsWindowList[l]->X != x) || (wsWindowList[l]->Y != y) || (wsWindowList[l]->Width != Event->xconfigure.width) || (wsWindowList[l]->Height != Event->xconfigure.height)) {
            wsWindowList[l]->X      = x;
            wsWindowList[l]->Y      = y;
            wsWindowList[l]->Width  = Event->xconfigure.width;
            wsWindowList[l]->Height = Event->xconfigure.height;

            if (wsWindowList[l]->ReSize)
                wsWindowList[l]->ReSize(wsWindowList[l]->X, wsWindowList[l]->Y, wsWindowList[l]->Width, wsWindowList[l]->Height);
        }

        wsWindowList[l]->Rolled = wsNone;

        if (Event->xconfigure.y < 0) {
            i = wsWindowRolled;
            wsWindowList[l]->Rolled = wsRolled;
            goto expose;
        }

        break;

    case KeyPress:
        i = wsKeyPressed;
        goto keypressed;

    case KeyRelease:
        i = wsKeyReleased;
keypressed:
        wsWindowList[l]->Alt      = 0;
        wsWindowList[l]->Shift    = 0;
        wsWindowList[l]->NumLock  = 0;
        wsWindowList[l]->Control  = 0;
        wsWindowList[l]->CapsLock = 0;

        if (Event->xkey.state & Mod1Mask)
            wsWindowList[l]->Alt = 1;

        if (Event->xkey.state & Mod2Mask)
            wsWindowList[l]->NumLock = 1;

        if (Event->xkey.state & ControlMask)
            wsWindowList[l]->Control = 1;

        if (Event->xkey.state & ShiftMask)
            wsWindowList[l]->Shift = 1;

        if (Event->xkey.state & LockMask)
            wsWindowList[l]->CapsLock = 1;

#if 0
        {
            KeySym keySym;
            keySym = XKeycodeToKeysym(wsDisplay, Event->xkey.keycode, 0);

            if (keySym != NoSymbol) {
                keySym = ((keySym & 0xff00) != 0 ? ((keySym & 0x00ff) + 256) : (keySym));
                wsKeyTable[keySym] = i;

                if (wsWindowList[l]->KeyHandler)
                    wsWindowList[l]->KeyHandler(Event->xkey.state, i, keySym);
            }
        }
#else
        {
            int key;
            char buf[100];
            KeySym keySym;
            static XComposeStatus stat;

            XLookupString(&Event->xkey, buf, sizeof(buf), &keySym, &stat);
            key = ((keySym & 0xff00) != 0 ? ((keySym & 0x00ff) + 256) : (keySym));
            wsKeyTable[key] = i;

            if (wsWindowList[l]->KeyHandler)
                wsWindowList[l]->KeyHandler(Event->xkey.keycode, i, key);
        }
#endif
        break;

    case MotionNotify:
        i = wsMoveMouse;
        {
            /* pump all motion events from the display queue:
             * this way it works faster when moving the window */
            static XEvent e;

            if (Event->xmotion.state) {
                while (XCheckTypedWindowEvent(display, Event->xany.window, MotionNotify, &e)) {
                    /* FIXME: need to make sure we didn't release/press the button in between...*/
                    /* FIXME: do we need some timeout here to make sure we don't spend too much time
                     * removing events from the queue? */
                    Event = &e;
                }
            }
        }
        if (wsWindowList[l]->wsCursor != None) {
            wsVisibleMouse(wsWindowList[l], wsShowMouseCursor);
            mouse_win  = wsWindowList[l];
            mouse_time = GetTimerMS();
        }
        goto buttonreleased;

    case ButtonRelease:
        i = Event->xbutton.button + 128;
        if (wsWindowList[l]->wsCursor != None) {
            wsVisibleMouse(wsWindowList[l], wsShowMouseCursor);
            mouse_win  = wsWindowList[l];
            mouse_time = GetTimerMS();
        }
        goto buttonreleased;

    case ButtonPress:
        i = Event->xbutton.button;
        if (wsWindowList[l]->wsCursor != None) {
            wsVisibleMouse(wsWindowList[l], wsShowMouseCursor);
            mouse_win  = wsWindowList[l];
            mouse_time = GetTimerMS();
        }
        goto buttonreleased;

    case EnterNotify:
        i = wsEnterWindow;
        goto buttonreleased;

    case LeaveNotify:
        i = wsLeaveWindow;
buttonreleased:

        if (wsWindowList[l]->MouseHandler)
            wsWindowList[l]->MouseHandler(i, Event->xbutton.x, Event->xbutton.y, Event->xmotion.x_root, Event->xmotion.y_root);

        break;

    case SelectionNotify:
        /* Handle DandD */
        wsXDNDProcessSelection(wsWindowList[l], Event);
        break;
    }

    XFlush(wsDisplay);
    XSync(wsDisplay, False);
    return !wsTrue;
}

void wsHandleEvents(void)
{
    /* handle pending events */
    while (XPending(wsDisplay)) {
        XNextEvent(wsDisplay, &wsEvent);
//   printf("### X event: %d  [%d]\n",wsEvent.type,delay);
        wsEvents(wsDisplay, &wsEvent);
    }
}

void wsMainLoop(void)
{
    int delay = 20;

    mp_msg(MSGT_GPLAYER, MSGL_V, "[ws] init threads: %d\n", XInitThreads());
    XSynchronize(wsDisplay, False);
    XLockDisplay(wsDisplay);
// XIfEvent( wsDisplay,&wsEvent,wsEvents );

    while (wsTrue) {
        /* handle pending events */
        while (XPending(wsDisplay)) {
            XNextEvent(wsDisplay, &wsEvent);
            wsEvents(wsDisplay, &wsEvent);
            delay = 0;
        }

        usleep(delay * 1000); // FIXME!

        if (delay < 10 * 20)
            delay += 20;               // pump up delay up to 0.2 sec (low activity)
    }

    XUnlockDisplay(wsDisplay);
}

// ----------------------------------------------------------------------------------------------
//    Move window to selected layer
// ----------------------------------------------------------------------------------------------

#define WIN_LAYER_ONBOTTOM               2
#define WIN_LAYER_NORMAL                 4
#define WIN_LAYER_ONTOP                 10

void wsSetLayer(Display *wsDisplay, Window win, int layer)
{
    vo_x11_setlayer(wsDisplay, win, layer);
}

/**
 * @brief Switch window fullscreen state.
 *
 *        Switch normal window to fullscreen and fullscreen window to normal.
 *
 * @param win pointer to a ws window structure
 */
void wsFullScreen(wsTWindow *win)
{
    if (win->isFullScreen) {
        if (vo_fs_type & vo_wm_FULLSCREEN)
            /* window manager supports EWMH */
            vo_x11_ewmh_fullscreen(win->WindowID, _NET_WM_STATE_REMOVE);
        else {
            win->X      = win->OldX;
            win->Y      = win->OldY;
            win->Width  = win->OldWidth;
            win->Height = win->OldHeight;
        }

        win->isFullScreen = False;
    } else {
        if (vo_fs_type & vo_wm_FULLSCREEN)
            /* window manager supports EWMH */
            vo_x11_ewmh_fullscreen(win->WindowID, _NET_WM_STATE_ADD);
        else {
            win->OldX      = win->X;
            win->OldY      = win->Y;
            win->OldWidth  = win->Width;
            win->OldHeight = win->Height;
        }

        win->isFullScreen = True;

        wsUpdateXineramaInfo(win);
    }

    /* unknown window manager and obsolete option -fsmode used */
    if (vo_wm_type == 0 && !(vo_fsmode & 16)) {
        XUnmapWindow(wsDisplay, win->WindowID); // required for MWM
        XWithdrawWindow(wsDisplay, win->WindowID, wsScreen);
    }

    /* restore window if window manager doesn't support EWMH */
    if (!(vo_fs_type & vo_wm_FULLSCREEN)) {
        wsWindowDecoration(win, win->Decorations && !win->isFullScreen);
        vo_x11_sizehint(win->X, win->Y, win->Width, win->Height, 0);
        wsSetLayer(wsDisplay, win->WindowID, win->isFullScreen);
        XMoveResizeWindow(wsDisplay, win->WindowID, win->X, win->Y, win->Width, win->Height);
    }

    /* some window managers lose ontop after fullscreen */
    if (!win->isFullScreen & vo_ontop)
        wsSetLayer(wsDisplay, win->WindowID, vo_ontop);

    wsRaiseWindowTop(wsDisplay, win->WindowID);
    XFlush(wsDisplay);
}

// ----------------------------------------------------------------------------------------------
//    Redraw screen.
// ----------------------------------------------------------------------------------------------
void wsPostRedisplay(wsTWindow *win)
{
    if (win->ReDraw) {
        win->State = wsWindowExpose;
        win->ReDraw();
        XFlush(wsDisplay);
    }
}

// ----------------------------------------------------------------------------------------------
//    Do Exit.
// ----------------------------------------------------------------------------------------------
void wsDoExit(void)
{
    wsTrue = False;
    wsResizeWindow(wsWindowList[0], 32, 32);
}

// ----------------------------------------------------------------------------------------------
//    Put 'Image' to window.
// ----------------------------------------------------------------------------------------------
void wsConvert(wsTWindow *win, unsigned char *Image)
{
    const uint8_t *src[4] = { Image, NULL, NULL, NULL };
    int src_stride[4]     = { 4 * win->xImage->width, 0, 0, 0 };
    uint8_t *dst[4]       = { win->ImageData, NULL, NULL, NULL };
    int dst_stride[4];
    int i;

    sws_ctx = sws_getCachedContext(sws_ctx, win->xImage->width, win->xImage->height, PIX_FMT_RGB32,
                                   win->xImage->width, win->xImage->height, out_pix_fmt,
                                   SWS_POINT, NULL, NULL, NULL);
    av_image_fill_linesizes(dst_stride, out_pix_fmt, win->xImage->width);
    sws_scale(sws_ctx, src, src_stride, 0, win->xImage->height, dst, dst_stride);

    if (!wsNonNativeOrder)
        return;

    switch (win->xImage->bits_per_pixel) {
    case 32:
    {
        uint32_t *d = (uint32_t *)win->ImageData;

        for (i = 0; i < win->xImage->width * win->xImage->height; i++)
            d[i] = bswap_32(d[i]);

        break;
    }

    case 16:
    case 15:
    {
        uint16_t *d = (uint16_t *)win->ImageData;

        for (i = 0; i < win->xImage->width * win->xImage->height; i++)
            d[i] = bswap_16(d[i]);

        break;
    }
    }
}

void wsPutImage(wsTWindow *win)
{
#ifdef HAVE_SHM
    if (wsUseXShm) {
        XShmPutImage(wsDisplay, win->WindowID, win->wGC, win->xImage,
                     0, 0,
                     (win->Width - win->xImage->width) / 2, (win->Height - win->xImage->height) / 2,
                     win->xImage->width, win->xImage->height, 0);
    } else
#endif
    {
        XPutImage(wsDisplay, win->WindowID, win->wGC, win->xImage,
                  0, 0,
                  (win->Width - win->xImage->width) / 2, (win->Height - win->xImage->height) / 2,
                  win->xImage->width, win->xImage->height);
    }
}

// ----------------------------------------------------------------------------------------------
//    Move window to x, y.
// ----------------------------------------------------------------------------------------------
void wsMoveWindow(wsTWindow *win, Bool abs, int x, int y)
{
    if (abs) {
        win->X = x;
        win->Y = y;
    } else
        wsWindowPosition(win, x, y, win->Width, win->Height);

    win->SizeHint.flags       = PPosition | PWinGravity;
    win->SizeHint.x           = win->X;
    win->SizeHint.y           = win->Y;
    win->SizeHint.win_gravity = StaticGravity;
    XSetWMNormalHints(wsDisplay, win->WindowID, &win->SizeHint);

    XMoveWindow(wsDisplay, win->WindowID, win->X, win->Y);

    if (win->ReSize)
        win->ReSize(win->X, win->Y, win->Width, win->Height);
}

/**
 * @brief Move the window to the x and y position, but if it no longer fits
 *        into the screen, reposition it towards the upper left.
 *
 * @param win pointer to a ws window structure
 * @param abs flag whether the position is real/absolute (True) or mock (False)
 * @param x x position of the window (real/absolute or mock)
 * @param y y position of the window (real/absolute or mock)
 */
void wsMoveWindowWithin(wsTWindow *win, Bool abs, int x, int y)
{
    Bool fitting = True;

    wsMoveWindow(win, abs, x, y);

    if (win->X + win->Width + 1 > wsMaxX) {
        fitting = False;
        win->X  = wsMaxX - win->Width;

        if (win->X < 0)
            win->X = 0;
    }

    if (win->Y + win->Height + 1 > wsMaxY) {
        fitting = False;
        win->Y  = wsMaxY - win->Height;

        if (win->Y < 0)
            win->Y = 0;
    }

    if (!fitting)
        wsMoveWindow(win, True, win->X, win->Y);
}

// ----------------------------------------------------------------------------------------------
//    Resize window to sx, sy.
// ----------------------------------------------------------------------------------------------
void wsResizeWindow(wsTWindow *win, int sx, int sy)
{
    win->Width  = sx;
    win->Height = sy;

    win->SizeHint.flags  = PPosition | PSize | PWinGravity; // | PBaseSize;
    win->SizeHint.x      = win->X;
    win->SizeHint.y      = win->Y;
    win->SizeHint.width  = win->Width;
    win->SizeHint.height = win->Height;

    if (win->Property & wsMinSize) {
        win->SizeHint.flags     |= PMinSize;
        win->SizeHint.min_width  = win->Width;
        win->SizeHint.min_height = win->Height;
    }

    if (win->Property & wsMaxSize) {
        win->SizeHint.flags     |= PMaxSize;
        win->SizeHint.max_width  = win->Width;
        win->SizeHint.max_height = win->Height;
    }

    win->SizeHint.win_gravity = StaticGravity;
    win->SizeHint.base_width  = sx;
    win->SizeHint.base_height = sy;

    if (vo_wm_type == 0)
        XUnmapWindow(wsDisplay, win->WindowID);

    XSetWMNormalHints(wsDisplay, win->WindowID, &win->SizeHint);
    XResizeWindow(wsDisplay, win->WindowID, sx, sy);

    if (win->ReSize)
        win->ReSize(win->X, win->Y, win->Width, win->Height);

    if (vo_wm_type == 0)
        XMapWindow(wsDisplay, win->WindowID);
}

/**
 * @brief Iconify a window.
 *
 * @param win pointer to a ws window structure
 */
void wsIconify(wsTWindow *win)
{
    XIconifyWindow(wsDisplay, win->WindowID, 0);
}

/**
 * @brief Map a window and raise it to the top.
 *
 * @param dpy display
 * @param win window
 */
void wsRaiseWindowTop(Display *dpy, Window win)
{
    XMapRaised(dpy, win);
    XRaiseWindow(dpy, win);
}

// ----------------------------------------------------------------------------------------------
//    Set window background to 'color'.
// ----------------------------------------------------------------------------------------------
void wsSetBackground(wsTWindow *win, int color)
{
    XSetWindowBackground(wsDisplay, win->WindowID, color);
}

void wsSetBackgroundRGB(wsTWindow *win, int r, int g, int b)
{
    int color = 0;

    switch (wsOutMask) {
    case wsRGB32:
    case wsRGB24:
        color = (r << 16) + (g << 8) + b;
        break;

    case wsBGR32:
    case wsBGR24:
        color = (b << 16) + (g << 8) + r;
        break;

    case wsRGB16:
        PACK_RGB16(b, g, r, color);
        break;

    case wsBGR16:
        PACK_RGB16(r, g, b, color);
        break;

    case wsRGB15:
        PACK_RGB15(b, g, r, color);
        break;

    case wsBGR15:
        PACK_RGB15(r, g, b, color);
        break;
    }

    XSetWindowBackground(wsDisplay, win->WindowID, color);
}

void wsSetForegroundRGB(wsTWindow *win, int r, int g, int b)
{
    int color = 0;

    switch (wsOutMask) {
    case wsRGB32:
    case wsRGB24:
        color = (r << 16) + (g << 8) + b;
        break;

    case wsBGR32:
    case wsBGR24:
        color = (b << 16) + (g << 8) + r;
        break;

    case wsRGB16:
        PACK_RGB16(b, g, r, color);
        break;

    case wsBGR16:
        PACK_RGB16(r, g, b, color);
        break;

    case wsRGB15:
        PACK_RGB15(b, g, r, color);
        break;

    case wsBGR15:
        PACK_RGB15(r, g, b, color);
        break;
    }

    XSetForeground(wsDisplay, win->wGC, color);
}

// ----------------------------------------------------------------------------------------------
//    Show / hide mouse cursor.
// ----------------------------------------------------------------------------------------------
void wsVisibleMouse(wsTWindow *win, int m)
{
    switch (m) {
    case wsShowMouseCursor:

        if (win->wsCursor != None) {
            XFreeCursor(wsDisplay, win->wsCursor);
            win->wsCursor = None;
        }

        XDefineCursor(wsDisplay, win->WindowID, 0);
        break;

    case wsHideMouseCursor:
        win->wsCursor = XCreatePixmapCursor(wsDisplay, win->wsCursorPixmap, win->wsCursorPixmap, &win->wsColor, &win->wsColor, 0, 0);
        XDefineCursor(wsDisplay, win->WindowID, win->wsCursor);
        break;
    }

    XFlush(wsDisplay);
}

int wsGetDepthOnScreen(void)
{
    int depth;
    XImage *mXImage;
    Visual *visual;

    if ((depth = vo_find_depth_from_visuals(wsDisplay, wsScreen, &visual)) > 0) {
        mXImage = XCreateImage(wsDisplay, visual, depth, ZPixmap, 0, NULL,
                               1, 1, 32, 0);
        wsDepthOnScreen = mXImage->bits_per_pixel;
        wsRedMask       = mXImage->red_mask;
        wsGreenMask     = mXImage->green_mask;
        wsBlueMask      = mXImage->blue_mask;
#if HAVE_BIGENDIAN
        wsNonNativeOrder = mXImage->byte_order == LSBFirst;
#else
        wsNonNativeOrder = mXImage->byte_order == MSBFirst;
#endif
        XDestroyImage(mXImage);
    } else {
        int bpp, ibpp;
        XWindowAttributes attribs;

        mXImage = XGetImage(wsDisplay, wsRootWin, 0, 0, 1, 1, AllPlanes, ZPixmap);
        bpp     = mXImage->bits_per_pixel;

        XGetWindowAttributes(wsDisplay, wsRootWin, &attribs);
        ibpp    = attribs.depth;
        mXImage = XGetImage(wsDisplay, wsRootWin, 0, 0, 1, 1, AllPlanes, ZPixmap);
        bpp     = mXImage->bits_per_pixel;

        if ((ibpp + 7) / 8 != (bpp + 7) / 8)
            ibpp = bpp;

        wsDepthOnScreen = ibpp;
        wsRedMask       = mXImage->red_mask;
        wsGreenMask     = mXImage->green_mask;
        wsBlueMask      = mXImage->blue_mask;
        XDestroyImage(mXImage);
    }

    return wsDepthOnScreen;
}

void wsXDone(void)
{
    XCloseDisplay(wsDisplay);
}

void wsVisibleWindow(wsTWindow *win, int show)
{
    switch (show) {
    case wsShowWindow:
        XMapRaised(wsDisplay, win->WindowID);
        if (vo_fs_type & vo_wm_FULLSCREEN)
            win->isFullScreen = False;
        break;

    case wsHideWindow:
        XUnmapWindow(wsDisplay, win->WindowID);
        break;
    }

    XFlush(wsDisplay);
}

void wsDestroyImage(wsTWindow *win)
{
    if (win->xImage) {
        XDestroyImage(win->xImage);

#ifdef HAVE_SHM
        if (wsUseXShm) {
            XShmDetach(wsDisplay, &win->Shminfo);
            shmdt(win->Shminfo.shmaddr);
        }
#endif
    }

    win->xImage = NULL;
}

void wsCreateImage(wsTWindow *win, int Width, int Height)
{
#ifdef HAVE_SHM
    if (wsUseXShm) {
        win->xImage = XShmCreateImage(wsDisplay, win->VisualInfo.visual,
                                      win->VisualInfo.depth, ZPixmap, NULL, &win->Shminfo, Width, Height);

        if (win->xImage == NULL) {
            mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_ShmError);
            mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0);
        }

        win->Shminfo.shmid = shmget(IPC_PRIVATE, win->xImage->bytes_per_line * win->xImage->height, IPC_CREAT | 0777);

        if (win->Shminfo.shmid < 0) {
            XDestroyImage(win->xImage);
            mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_ShmError);
            mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0);
        }

        win->Shminfo.shmaddr = (char *)shmat(win->Shminfo.shmid, 0, 0);

        if (win->Shminfo.shmaddr == ((char *)-1)) {
            XDestroyImage(win->xImage);

            if (win->Shminfo.shmaddr != ((char *)-1))
                shmdt(win->Shminfo.shmaddr);

            mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_ShmError);
            mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0);
        }

        win->xImage->data     = win->Shminfo.shmaddr;
        win->Shminfo.readOnly = 0;
        XShmAttach(wsDisplay, &win->Shminfo);
        XSync(wsDisplay, False);
        shmctl(win->Shminfo.shmid, IPC_RMID, 0);
    } else
#endif
    {
        win->xImage = XCreateImage(wsDisplay, win->VisualInfo.visual, win->VisualInfo.depth,
                                   ZPixmap, 0, 0, Width, Height,
                                   (wsDepthOnScreen == 3) ? 32 : wsDepthOnScreen,
                                   0);

        if ((win->xImage->data = malloc(win->xImage->bytes_per_line * win->xImage->height)) == NULL) {
            mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_NotEnoughMemoryDrawBuffer);
            mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0);
        }
    }

    win->ImageData   = (unsigned char *)win->xImage->data;
    win->ImageDataw  = (unsigned short int *)win->xImage->data;
    win->ImageDatadw = (unsigned int *)win->xImage->data;
}

void wsResizeImage(wsTWindow *win, int Width, int Height)
{
    wsDestroyImage(win);
    wsCreateImage(win, Width, Height);
}

int wsGetOutMask(void)
{
    if ((wsDepthOnScreen == 32) && (wsRedMask == 0xff0000) && (wsGreenMask == 0x00ff00) && (wsBlueMask == 0x0000ff))
        return wsRGB32;

    if ((wsDepthOnScreen == 32) && (wsRedMask == 0x0000ff) && (wsGreenMask == 0x00ff00) && (wsBlueMask == 0xff0000))
        return wsBGR32;

    if ((wsDepthOnScreen == 24) && (wsRedMask == 0xff0000) && (wsGreenMask == 0x00ff00) && (wsBlueMask == 0x0000ff))
        return wsRGB24;

    if ((wsDepthOnScreen == 24) && (wsRedMask == 0x0000ff) && (wsGreenMask == 0x00ff00) && (wsBlueMask == 0xff0000))
        return wsBGR24;

    if ((wsDepthOnScreen == 16) && (wsRedMask == 0xf800) && (wsGreenMask == 0x7e0) && (wsBlueMask == 0x1f))
        return wsRGB16;

    if ((wsDepthOnScreen == 16) && (wsRedMask == 0x1f) && (wsGreenMask == 0x7e0) && (wsBlueMask == 0xf800))
        return wsBGR16;

    if ((wsDepthOnScreen == 15) && (wsRedMask == 0x7c00) && (wsGreenMask == 0x3e0) && (wsBlueMask == 0x1f))
        return wsRGB15;

    if ((wsDepthOnScreen == 15) && (wsRedMask == 0x1f) && (wsGreenMask == 0x3e0) && (wsBlueMask == 0x7c00))
        return wsBGR15;

    return 0;
}

void wsSetTitle(wsTWindow *win, char *name)
{
    XStoreName(wsDisplay, win->WindowID, name);
}

void wsSetMousePosition(wsTWindow *win, int x, int y)
{
    XWarpPointer(wsDisplay, wsRootWin, win->WindowID, 0, 0, 0, 0, x, y);
}

void wsSetShape(wsTWindow *win, char *data)
{
#ifdef CONFIG_XSHAPE
    if (!wsUseXShape)
        return;

    if (data) {
        win->Mask = XCreateBitmapFromData(wsDisplay, win->WindowID, data, win->Width, win->Height);
        XShapeCombineMask(wsDisplay, win->WindowID, ShapeBounding, 0, 0, win->Mask, ShapeSet);
        XFreePixmap(wsDisplay, win->Mask);
    } else
        XShapeCombineMask(wsDisplay, win->WindowID, ShapeBounding, 0, 0, None, ShapeSet);
#endif
}

/**
 * @brief Set differently sized icons to a window.
 *
 *        This function sets the X icon hint as well as
 *        the properties KWM_WIN_ICON and _NET_WM_ICON.
 *
 * @param dpy display
 * @param win window
 * @param icon pointer to the icons
 */
void wsSetIcon(Display *dpy, Window win, guiIcon_t *icon)
{
    XWMHints *wm;
    Atom iconatom;
    long data[2];

    if (icon->normal) {
        wm = XGetWMHints(dpy, win);

        if (!wm)
            wm = XAllocWMHints();

        wm->icon_pixmap = icon->normal;
        wm->icon_mask   = icon->normal_mask;
        wm->flags      |= IconPixmapHint | IconMaskHint;

        XSetWMHints(dpy, win, wm);
        XFree(wm);
    }

    if (icon->small || icon->normal) {
        iconatom = XInternAtom(dpy, "KWM_WIN_ICON", False);
        data[0]  = (icon->small ? icon->small : icon->normal);
        data[1]  = (icon->small ? icon->small_mask : icon->normal_mask);

        XChangeProperty(dpy, win, iconatom, iconatom, 32, PropModeReplace, (unsigned char *)data, 2);
    }

    if (icon->collection) {
        iconatom = XInternAtom(dpy, "_NET_WM_ICON", False);
        XChangeProperty(dpy, win, iconatom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)icon->collection, icon->collection_size);
    }
}