src/video/x11/SDL_x11mouse.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 24 Nov 2013 23:56:17 -0500
changeset 8093 b43765095a6f
parent 7827 a03ec8de0426
child 8149 681eb46b8ac4
permissions -rw-r--r--
Make internal SDL sources include SDL_internal.h instead of SDL_config.h

The new header will include SDL_config.h, but allows for other global stuff.
slouken@1950
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@6885
     3
  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
slouken@1950
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@1950
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@1950
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@1950
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@5471
    22
slouken@5481
    23
#if SDL_VIDEO_DRIVER_X11
slouken@5481
    24
mikesart@6675
    25
#include <X11/cursorfont.h>
slouken@5471
    26
#include "SDL_assert.h"
slouken@1950
    27
#include "SDL_x11video.h"
slouken@2940
    28
#include "SDL_x11mouse.h"
dimitris@6316
    29
#include "SDL_x11xinput2.h"
slouken@1950
    30
#include "../../events/SDL_mouse_c.h"
slouken@1950
    31
slouken@5470
    32
slouken@5470
    33
/* FIXME: Find a better place to put this... */
slouken@5470
    34
static Cursor x11_empty_cursor = None;
slouken@5470
    35
slouken@5470
    36
static Display *
slouken@5470
    37
GetDisplay(void)
slouken@5470
    38
{
slouken@5470
    39
    return ((SDL_VideoData *)SDL_GetVideoDevice()->driverdata)->display;
slouken@5470
    40
}
slouken@5470
    41
slouken@5470
    42
static Cursor
slouken@5470
    43
X11_CreateEmptyCursor()
slouken@5470
    44
{
slouken@5470
    45
    if (x11_empty_cursor == None) {
slouken@5470
    46
        Display *display = GetDisplay();
slouken@5470
    47
        char data[1];
slouken@5470
    48
        XColor color;
slouken@5470
    49
        Pixmap pixmap;
slouken@5470
    50
slouken@5470
    51
        SDL_zero(data);
slouken@5470
    52
        color.red = color.green = color.blue = 0;
icculus@7827
    53
        pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
slouken@5470
    54
                                       data, 1, 1);
slouken@5470
    55
        if (pixmap) {
icculus@7827
    56
            x11_empty_cursor = X11_XCreatePixmapCursor(display, pixmap, pixmap,
slouken@5470
    57
                                                   &color, &color, 0, 0);
icculus@7827
    58
            X11_XFreePixmap(display, pixmap);
slouken@5470
    59
        }
slouken@5470
    60
    }
slouken@5470
    61
    return x11_empty_cursor;
slouken@5470
    62
}
slouken@5470
    63
slouken@5470
    64
static void
slouken@5470
    65
X11_DestroyEmptyCursor(void)
slouken@5470
    66
{
slouken@5470
    67
    if (x11_empty_cursor != None) {
icculus@7827
    68
        X11_XFreeCursor(GetDisplay(), x11_empty_cursor);
slouken@5470
    69
        x11_empty_cursor = None;
slouken@5470
    70
    }
slouken@5470
    71
}
slouken@5470
    72
slouken@5470
    73
static SDL_Cursor *
slouken@5470
    74
X11_CreateDefaultCursor()
slouken@5470
    75
{
slouken@5470
    76
    SDL_Cursor *cursor;
slouken@5470
    77
slouken@5470
    78
    cursor = SDL_calloc(1, sizeof(*cursor));
slouken@5470
    79
    if (cursor) {
slouken@5470
    80
        /* None is used to indicate the default cursor */
slouken@5470
    81
        cursor->driverdata = (void*)None;
slouken@5470
    82
    } else {
slouken@5470
    83
        SDL_OutOfMemory();
slouken@5470
    84
    }
slouken@5470
    85
slouken@5470
    86
    return cursor;
slouken@5470
    87
}
slouken@5470
    88
slouken@5471
    89
#if SDL_VIDEO_DRIVER_X11_XCURSOR
slouken@5471
    90
static Cursor
slouken@5471
    91
X11_CreateXCursorCursor(SDL_Surface * surface, int hot_x, int hot_y)
slouken@5471
    92
{
slouken@5471
    93
    Display *display = GetDisplay();
slouken@5471
    94
    Cursor cursor = None;
slouken@5471
    95
    XcursorImage *image;
slouken@5471
    96
icculus@7827
    97
    image = X11_XcursorImageCreate(surface->w, surface->h);
slouken@5471
    98
    if (!image) {
slouken@5471
    99
        SDL_OutOfMemory();
slouken@5471
   100
        return None;
slouken@5471
   101
    }
slouken@5471
   102
    image->xhot = hot_x;
slouken@5471
   103
    image->yhot = hot_y;
slouken@5471
   104
    image->delay = 0;
slouken@5471
   105
slouken@5471
   106
    SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
slouken@5471
   107
    SDL_assert(surface->pitch == surface->w * 4);
slouken@5471
   108
    SDL_memcpy(image->pixels, surface->pixels, surface->h * surface->pitch);
slouken@5471
   109
icculus@7827
   110
    cursor = X11_XcursorImageLoadCursor(display, image);
slouken@5471
   111
icculus@7827
   112
    X11_XcursorImageDestroy(image);
slouken@5471
   113
slouken@5471
   114
    return cursor;
slouken@5471
   115
}
slouken@5471
   116
#endif /* SDL_VIDEO_DRIVER_X11_XCURSOR */
slouken@5471
   117
slouken@5470
   118
static Cursor
slouken@5470
   119
X11_CreatePixmapCursor(SDL_Surface * surface, int hot_x, int hot_y)
slouken@5470
   120
{
slouken@5470
   121
    Display *display = GetDisplay();
slouken@5470
   122
    XColor fg, bg;
slouken@5470
   123
    Cursor cursor = None;
slouken@5470
   124
    Uint32 *ptr;
slouken@5470
   125
    Uint8 *data_bits, *mask_bits;
slouken@5470
   126
    Pixmap data_pixmap, mask_pixmap;
slouken@5470
   127
    int x, y;
slouken@5470
   128
    unsigned int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits;
slouken@5470
   129
    unsigned int width_bytes = ((surface->w + 7) & ~7) / 8;
slouken@5470
   130
slouken@5470
   131
    data_bits = SDL_calloc(1, surface->h * width_bytes);
slouken@7071
   132
    if (!data_bits) {
slouken@7071
   133
        SDL_OutOfMemory();
slouken@7071
   134
        return None;
slouken@7071
   135
    }
slouken@7071
   136
slouken@5470
   137
    mask_bits = SDL_calloc(1, surface->h * width_bytes);
slouken@7071
   138
    if (!mask_bits) {
slouken@7071
   139
        SDL_free(data_bits);
slouken@5470
   140
        SDL_OutOfMemory();
slouken@5470
   141
        return None;
slouken@5470
   142
    }
slouken@5470
   143
slouken@5471
   144
    /* Code below assumes ARGB pixel format */
slouken@5471
   145
    SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
slouken@5471
   146
icculus@5981
   147
    rfg = gfg = bfg = rbg = gbg = bbg = fgBits = bgBits = 0;
slouken@5470
   148
    for (y = 0; y < surface->h; ++y) {
slouken@5470
   149
        ptr = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch);
slouken@5470
   150
        for (x = 0; x < surface->w; ++x) {
slouken@5470
   151
            int alpha = (*ptr >> 24) & 0xff;
slouken@5470
   152
            int red   = (*ptr >> 16) & 0xff;
slouken@5470
   153
            int green = (*ptr >> 8) & 0xff;
slouken@5470
   154
            int blue  = (*ptr >> 0) & 0xff;
slouken@5470
   155
            if (alpha > 25) {
slouken@5470
   156
                mask_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
slouken@5470
   157
slouken@5470
   158
                if ((red + green + blue) > 0x40) {
slouken@5470
   159
                    fgBits++;
slouken@5470
   160
                    rfg += red;
slouken@5470
   161
                    gfg += green;
slouken@5470
   162
                    bfg += blue;
slouken@5470
   163
                    data_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
slouken@5470
   164
                } else {
slouken@5470
   165
                    bgBits++;
slouken@5470
   166
                    rbg += red;
slouken@5470
   167
                    gbg += green;
slouken@5470
   168
                    bbg += blue;
slouken@5470
   169
                }
slouken@5470
   170
            }
slouken@5470
   171
            ++ptr;
slouken@5470
   172
        }
slouken@5470
   173
    }
slouken@5470
   174
slouken@5470
   175
    if (fgBits) {
slouken@5470
   176
        fg.red   = rfg * 257 / fgBits;
slouken@5470
   177
        fg.green = gfg * 257 / fgBits;
slouken@5470
   178
        fg.blue  = bfg * 257 / fgBits;
slouken@5470
   179
    }
slouken@5470
   180
    else fg.red = fg.green = fg.blue = 0;
slouken@5470
   181
slouken@5470
   182
    if (bgBits) {
slouken@5470
   183
        bg.red   = rbg * 257 / bgBits;
slouken@5470
   184
        bg.green = gbg * 257 / bgBits;
slouken@5470
   185
        bg.blue  = bbg * 257 / bgBits;
slouken@5470
   186
    }
slouken@5470
   187
    else bg.red = bg.green = bg.blue = 0;
slouken@5470
   188
icculus@7827
   189
    data_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
slouken@5505
   190
                                        (char*)data_bits,
slouken@5505
   191
                                        surface->w, surface->h);
icculus@7827
   192
    mask_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
slouken@5505
   193
                                        (char*)mask_bits,
slouken@5505
   194
                                        surface->w, surface->h);
icculus@7827
   195
    cursor = X11_XCreatePixmapCursor(display, data_pixmap, mask_pixmap,
slouken@5470
   196
                                 &fg, &bg, hot_x, hot_y);
icculus@7827
   197
    X11_XFreePixmap(display, data_pixmap);
icculus@7827
   198
    X11_XFreePixmap(display, mask_pixmap);
slouken@5470
   199
slouken@5470
   200
    return cursor;
slouken@5470
   201
}
slouken@5470
   202
slouken@5470
   203
static SDL_Cursor *
slouken@5470
   204
X11_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
slouken@5470
   205
{
slouken@5470
   206
    SDL_Cursor *cursor;
slouken@5470
   207
slouken@5470
   208
    cursor = SDL_calloc(1, sizeof(*cursor));
slouken@5470
   209
    if (cursor) {
slouken@5471
   210
        Cursor x11_cursor = None;
slouken@5471
   211
slouken@5471
   212
#if SDL_VIDEO_DRIVER_X11_XCURSOR
slouken@5471
   213
        if (SDL_X11_HAVE_XCURSOR) {
slouken@5471
   214
            x11_cursor = X11_CreateXCursorCursor(surface, hot_x, hot_y);
slouken@5471
   215
        }
slouken@5471
   216
#endif
slouken@5471
   217
        if (x11_cursor == None) {
slouken@5471
   218
            x11_cursor = X11_CreatePixmapCursor(surface, hot_x, hot_y);
slouken@5471
   219
        }
slouken@5471
   220
        cursor->driverdata = (void*)x11_cursor;
slouken@5470
   221
    } else {
slouken@5470
   222
        SDL_OutOfMemory();
slouken@5470
   223
    }
slouken@5470
   224
slouken@5470
   225
    return cursor;
slouken@5470
   226
}
slouken@5470
   227
mikesart@6675
   228
static SDL_Cursor *
mikesart@6675
   229
X11_CreateSystemCursor(SDL_SystemCursor id)
mikesart@6675
   230
{
mikesart@6675
   231
    SDL_Cursor *cursor;
mikesart@6675
   232
    unsigned int shape;
mikesart@6675
   233
mikesart@6675
   234
    switch(id)
mikesart@6675
   235
    {
mikesart@6675
   236
    default:
mikesart@6675
   237
        SDL_assert(0);
mikesart@6675
   238
        return NULL;
slouken@7191
   239
    /* X Font Cursors reference: */
slouken@7191
   240
    /*   http://tronche.com/gui/x/xlib/appendix/b/ */
mikesart@6834
   241
    case SDL_SYSTEM_CURSOR_ARROW:     shape = XC_left_ptr; break;
mikesart@6675
   242
    case SDL_SYSTEM_CURSOR_IBEAM:     shape = XC_xterm; break;
mikesart@6675
   243
    case SDL_SYSTEM_CURSOR_WAIT:      shape = XC_watch; break;
mikesart@6675
   244
    case SDL_SYSTEM_CURSOR_CROSSHAIR: shape = XC_tcross; break;
mikesart@6675
   245
    case SDL_SYSTEM_CURSOR_WAITARROW: shape = XC_watch; break;
mikesart@6675
   246
    case SDL_SYSTEM_CURSOR_SIZENWSE:  shape = XC_fleur; break;
mikesart@6675
   247
    case SDL_SYSTEM_CURSOR_SIZENESW:  shape = XC_fleur; break;
mikesart@6675
   248
    case SDL_SYSTEM_CURSOR_SIZEWE:    shape = XC_sb_h_double_arrow; break;
mikesart@6675
   249
    case SDL_SYSTEM_CURSOR_SIZENS:    shape = XC_sb_v_double_arrow; break;
mikesart@6675
   250
    case SDL_SYSTEM_CURSOR_SIZEALL:   shape = XC_fleur; break;
mikesart@6675
   251
    case SDL_SYSTEM_CURSOR_NO:        shape = XC_pirate; break;
mikesart@6675
   252
    case SDL_SYSTEM_CURSOR_HAND:      shape = XC_hand2; break;
mikesart@6675
   253
    }
mikesart@6675
   254
mikesart@6675
   255
    cursor = SDL_calloc(1, sizeof(*cursor));
mikesart@6675
   256
    if (cursor) {
mikesart@6675
   257
        Cursor x11_cursor;
mikesart@6675
   258
icculus@7827
   259
        x11_cursor = X11_XCreateFontCursor(GetDisplay(), shape);
mikesart@6675
   260
mikesart@6675
   261
        cursor->driverdata = (void*)x11_cursor;
mikesart@6675
   262
    } else {
mikesart@6675
   263
        SDL_OutOfMemory();
mikesart@6675
   264
    }
mikesart@6675
   265
mikesart@6675
   266
    return cursor;
mikesart@6675
   267
}
mikesart@6675
   268
slouken@5470
   269
static void
slouken@5470
   270
X11_FreeCursor(SDL_Cursor * cursor)
slouken@5470
   271
{
slouken@5470
   272
    Cursor x11_cursor = (Cursor)cursor->driverdata;
slouken@5470
   273
slouken@5470
   274
    if (x11_cursor != None) {
icculus@7827
   275
        X11_XFreeCursor(GetDisplay(), x11_cursor);
slouken@5470
   276
    }
slouken@5470
   277
    SDL_free(cursor);
slouken@5470
   278
}
slouken@5470
   279
slouken@5470
   280
static int
slouken@5470
   281
X11_ShowCursor(SDL_Cursor * cursor)
slouken@5470
   282
{
slouken@5470
   283
    Cursor x11_cursor = 0;
slouken@5470
   284
slouken@5470
   285
    if (cursor) {
slouken@5470
   286
        x11_cursor = (Cursor)cursor->driverdata;
slouken@5470
   287
    } else {
slouken@5470
   288
        x11_cursor = X11_CreateEmptyCursor();
slouken@5470
   289
    }
slouken@5470
   290
slouken@5470
   291
    /* FIXME: Is there a better way than this? */
slouken@5470
   292
    {
slouken@5470
   293
        SDL_VideoDevice *video = SDL_GetVideoDevice();
slouken@5470
   294
        Display *display = GetDisplay();
slouken@5470
   295
        SDL_Window *window;
slouken@5470
   296
        SDL_WindowData *data;
slouken@5470
   297
slouken@5470
   298
        for (window = video->windows; window; window = window->next) {
slouken@5470
   299
            data = (SDL_WindowData *)window->driverdata;
slouken@5470
   300
            if (x11_cursor != None) {
icculus@7827
   301
                X11_XDefineCursor(display, data->xwindow, x11_cursor);
slouken@5470
   302
            } else {
icculus@7827
   303
                X11_XUndefineCursor(display, data->xwindow);
slouken@5470
   304
            }
slouken@5470
   305
        }
icculus@7827
   306
        X11_XFlush(display);
slouken@5470
   307
    }
slouken@5470
   308
    return 0;
slouken@5470
   309
}
slouken@5470
   310
slouken@5470
   311
static void
slouken@5470
   312
X11_WarpMouse(SDL_Window * window, int x, int y)
slouken@5470
   313
{
slouken@5470
   314
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
slouken@5470
   315
    Display *display = data->videodata->display;
slouken@5470
   316
icculus@7827
   317
    X11_XWarpPointer(display, None, data->xwindow, 0, 0, 0, 0, x, y);
icculus@7827
   318
    X11_XSync(display, False);
slouken@5470
   319
}
slouken@5470
   320
slouken@5470
   321
static int
slouken@5470
   322
X11_SetRelativeMouseMode(SDL_bool enabled)
slouken@5470
   323
{
slouken@6311
   324
#if SDL_VIDEO_DRIVER_X11_XINPUT2
dimitris@6316
   325
    if(X11_Xinput2IsInitialized())
dimitris@6316
   326
        return 0;
slouken@6311
   327
#else
slouken@5470
   328
    SDL_Unsupported();
dimitris@6316
   329
#endif
slouken@5470
   330
    return -1;
slouken@5470
   331
}
slouken@5470
   332
slouken@1950
   333
void
slouken@1950
   334
X11_InitMouse(_THIS)
slouken@1950
   335
{
slouken@5470
   336
    SDL_Mouse *mouse = SDL_GetMouse();
slouken@5470
   337
slouken@5470
   338
    mouse->CreateCursor = X11_CreateCursor;
slouken@7191
   339
    mouse->CreateSystemCursor = X11_CreateSystemCursor;
slouken@5470
   340
    mouse->ShowCursor = X11_ShowCursor;
slouken@5470
   341
    mouse->FreeCursor = X11_FreeCursor;
slouken@5470
   342
    mouse->WarpMouse = X11_WarpMouse;
slouken@5470
   343
    mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
slouken@5470
   344
slouken@5470
   345
    SDL_SetDefaultCursor(X11_CreateDefaultCursor());
slouken@1950
   346
}
slouken@1950
   347
slouken@1950
   348
void
slouken@1950
   349
X11_QuitMouse(_THIS)
slouken@1950
   350
{
slouken@5470
   351
    X11_DestroyEmptyCursor();
slouken@1950
   352
}
slouken@1950
   353
slouken@5481
   354
#endif /* SDL_VIDEO_DRIVER_X11 */
slouken@5481
   355
slouken@1950
   356
/* vi: set ts=4 sw=4 expandtab: */