cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_x11mouse.c (12221B)


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