mouse.c 21.9 KB
Newer Older
1
/*
2
 * X11 mouse driver
3
 *
4
 * Copyright 1998 Ulrich Weigand
5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 20 21 22
 */

#include "config.h"

23
#include <X11/Xlib.h>
24
#ifdef HAVE_LIBXXF86DGA2
25
#include <X11/extensions/xf86dga.h>
26
#endif
27
#include <stdarg.h>
28

29 30
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
31
#include "windef.h"
32
#include "winbase.h"
33 34
#include "wine/winuser16.h"

35
#include "x11drv.h"
36
#include "wine/debug.h"
37

38
WINE_DEFAULT_DEBUG_CHANNEL(cursor);
39

40 41
/**********************************************************************/

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
#define NB_BUTTONS   5     /* Windows can handle 3 buttons and the wheel too */

static const UINT button_down_flags[NB_BUTTONS] =
{
    MOUSEEVENTF_LEFTDOWN,
    MOUSEEVENTF_MIDDLEDOWN,
    MOUSEEVENTF_RIGHTDOWN,
    MOUSEEVENTF_WHEEL,
    MOUSEEVENTF_WHEEL
};

static const UINT button_up_flags[NB_BUTTONS] =
{
    MOUSEEVENTF_LEFTUP,
    MOUSEEVENTF_MIDDLEUP,
    MOUSEEVENTF_RIGHTUP,
    0,
    0
};

static BYTE *pKeyStateTable;


/***********************************************************************
 *		get_coords
 *
 * get the coordinates of a mouse event
 */
static void get_coords( HWND *hwnd, Window window, int x, int y, POINT *pt )
{
    struct x11drv_win_data *data;
    WND *win;

    if (!(win = WIN_GetPtr( *hwnd )) || win == WND_OTHER_PROCESS) return;
    data = win->pDriverData;

    if (window == data->whole_window)
    {
        x -= data->client_rect.left;
        y -= data->client_rect.top;
    }
    WIN_ReleasePtr( win );

    pt->x = x;
    pt->y = y;
    if (*hwnd != GetDesktopWindow())
    {
        ClientToScreen( *hwnd, pt );
        *hwnd = GetAncestor( *hwnd, GA_ROOT );
    }
}


/***********************************************************************
96
 *		update_button_state
97
 *
98
 * Update the button state with what X provides us
99
 */
100
static void update_button_state( unsigned int state )
101 102 103 104
{
    pKeyStateTable[VK_LBUTTON] = (state & Button1Mask ? 0x80 : 0);
    pKeyStateTable[VK_MBUTTON] = (state & Button2Mask ? 0x80 : 0);
    pKeyStateTable[VK_RBUTTON] = (state & Button3Mask ? 0x80 : 0);
105 106 107 108 109 110 111 112 113 114
}


/***********************************************************************
 *		update_key_state
 *
 * Update the key state with what X provides us
 */
static void update_key_state( unsigned int state )
{
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    pKeyStateTable[VK_SHIFT]   = (state & ShiftMask   ? 0x80 : 0);
    pKeyStateTable[VK_CONTROL] = (state & ControlMask ? 0x80 : 0);
}


/***********************************************************************
 *		send_mouse_event
 */
static void send_mouse_event( HWND hwnd, DWORD flags, DWORD posX, DWORD posY,
                              DWORD data, Time time )
{
    INPUT input;

    TRACE("(%04lX,%ld,%ld)\n", flags, posX, posY );

    if (flags & MOUSEEVENTF_ABSOLUTE)
    {
        int width  = GetSystemMetrics( SM_CXSCREEN );
        int height = GetSystemMetrics( SM_CYSCREEN );
        /* Relative mouse movements seem not to be scaled as absolute ones */
        posX = (((long)posX << 16) + width-1)  / width;
        posY = (((long)posY << 16) + height-1) / height;
    }

    input.type             = WINE_INTERNAL_INPUT_MOUSE;
    input.u.mi.dx          = posX;
    input.u.mi.dy          = posY;
    input.u.mi.mouseData   = data;
    input.u.mi.dwFlags     = flags;
    input.u.mi.time        = time - X11DRV_server_startticks;
    input.u.mi.dwExtraInfo = (ULONG_PTR)hwnd;
    SendInput( 1, &input, sizeof(input) );
}

149 150

/***********************************************************************
151 152 153
 *		update_cursor
 *
 * Update the cursor of a window on a mouse event.
154
 */
155 156 157 158 159 160 161 162 163 164
static void update_cursor( HWND hwnd, Window win )
{
    struct x11drv_thread_data *data = x11drv_thread_data();

    if (win == X11DRV_get_client_window( hwnd ))
        win = X11DRV_get_whole_window( hwnd );  /* always set cursor on whole window */

    if (data->cursor_window != win)
    {
        data->cursor_window = win;
165 166 167
        wine_tsx11_lock();
        if (data->cursor) XDefineCursor( data->display, win, data->cursor );
        wine_tsx11_unlock();
168 169 170 171 172 173 174 175 176 177
    }
}


/***********************************************************************
 *		create_cursor
 *
 * Create an X cursor from a Windows one.
 */
static Cursor create_cursor( Display *display, CURSORICONINFO *ptr )
178
{
179
    Pixmap pixmapBits, pixmapMask, pixmapMaskInv, pixmapAll;
180 181 182 183 184 185 186 187
    XColor fg, bg;
    Cursor cursor = None;

    if (!ptr)  /* Create an empty cursor */
    {
        static const char data[] = { 0 };

        bg.red = bg.green = bg.blue = 0x0000;
188
        pixmapBits = XCreateBitmapFromData( display, root_window, data, 1, 1 );
189 190 191 192 193 194 195 196 197 198
        if (pixmapBits)
        {
            cursor = XCreatePixmapCursor( display, pixmapBits, pixmapBits,
                                          &bg, &bg, 0, 0 );
            XFreePixmap( display, pixmapBits );
        }
    }
    else  /* Create the X cursor from the bits */
    {
        XImage *image;
199
        GC gc;
200

201 202 203
        TRACE("Bitmap %dx%d planes=%d bpp=%d bytesperline=%d\n",
            ptr->nWidth, ptr->nHeight, ptr->bPlanes, ptr->bBitsPerPixel,
            ptr->nWidthBytes);
204 205 206 207 208 209 210
        /* Create a pixmap and transfer all the bits to it */

        /* NOTE: Following hack works, but only because XFree depth
         *       1 images really use 1 bit/pixel (and so the same layout
         *       as the Windows cursor data). Perhaps use a more generic
         *       algorithm here.
         */
211 212 213
        /* This pixmap will be written with two bitmaps. The first is
         *  the mask and the second is the image.
         */
214
        if (!(pixmapAll = XCreatePixmap( display, root_window,
215
                  ptr->nWidth, ptr->nHeight * 2, 1 )))
216
            return 0;
217
        if (!(image = XCreateImage( display, visual,
218
                1, ZPixmap, 0, (char *)(ptr + 1), ptr->nWidth,
219
                ptr->nHeight * 2, 16, ptr->nWidthBytes/ptr->bBitsPerPixel)))
220 221 222 223
        {
            XFreePixmap( display, pixmapAll );
            return 0;
        }
224 225 226 227 228 229
        gc = XCreateGC( display, pixmapAll, 0, NULL );
        XSetGraphicsExposures( display, gc, False );
        image->byte_order = MSBFirst;
        image->bitmap_bit_order = MSBFirst;
        image->bitmap_unit = 16;
        _XInitImageFuncPtrs(image);
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
        if (ptr->bPlanes * ptr->bBitsPerPixel == 1)
        {
            /* A plain old white on black cursor. */
            fg.red = fg.green = fg.blue = 0xffff;
            bg.red = bg.green = bg.blue = 0x0000;
            XPutImage( display, pixmapAll, gc, image,
                0, 0, 0, 0, ptr->nWidth, ptr->nHeight * 2 );
        }
        else
        {
            int     rbits, gbits, bbits, red, green, blue;
            int     rfg, gfg, bfg, rbg, gbg, bbg;
            int     rscale, gscale, bscale;
            int     x, y, xmax, ymax, bitIndex, byteIndex, xorIndex;
            unsigned char *theMask, *theImage, theChar;
            int     threshold, fgBits, bgBits, bitShifted;
            BYTE    pXorBits[128];   /* Up to 32x32 icons */
247

248 249 250 251 252 253 254 255
            switch (ptr->bBitsPerPixel)
            {
            case 24:
                rbits = 8;
                gbits = 8;
                bbits = 8;
                threshold = 0x40;
                break;
256 257 258 259 260 261
            case 16:
                rbits = 5;
                gbits = 6;
                bbits = 5;
                threshold = 0x40;
                break;
262 263 264 265 266 267 268 269 270 271 272 273
            default:
                FIXME("Currently no support for cursors with %d bits per pixel\n",
                  ptr->bBitsPerPixel);
                XFreePixmap( display, pixmapAll );
                XFreeGC( display, gc );
                image->data = NULL;
                XDestroyImage( image );
                return 0;
            }
            /* The location of the mask. */
            theMask = (char *)(ptr + 1);
            /* The mask should still be 1 bit per pixel. The color image
274
             * should immediately follow the mask.
275 276 277 278 279 280 281 282 283 284 285 286 287 288
             */
            theImage = &theMask[ptr->nWidth/8 * ptr->nHeight];
            rfg = gfg = bfg = rbg = gbg = bbg = 0;
            bitIndex = 0;
            byteIndex = 0;
            xorIndex = 0;
            fgBits = 0;
            bitShifted = 0x01;
            xmax = (ptr->nWidth > 32) ? 32 : ptr->nWidth;
            if (ptr->nWidth > 32) {
                ERR("Got a %dx%d cursor. Cannot handle larger than 32x32.\n",
                  ptr->nWidth, ptr->nHeight);
            }
            ymax = (ptr->nHeight > 32) ? 32 : ptr->nHeight;
289

290 291 292 293 294
            memset(pXorBits, 0, 128);
            for (y=0; y<ymax; y++)
            {
                for (x=0; x<xmax; x++)
                {
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
                   	red = green = blue = 0;
                   	switch (ptr->bBitsPerPixel)
                   	{
                   	case 24:
                   	    theChar = theImage[byteIndex++];
                   	    blue = theChar;
                   	    theChar = theImage[byteIndex++];
                   	    green = theChar;
                   	    theChar = theImage[byteIndex++];
                   	    red = theChar;
                   	    break;
                   	case 16:
                   	    theChar = theImage[byteIndex++];
                   	    blue = theChar & 0x1F;
                   	    green = (theChar & 0xE0) >> 5;
                   	    theChar = theImage[byteIndex++];
                   	    green |= (theChar & 0x07) << 3;
                   	    red = (theChar & 0xF8) >> 3;
                   	    break;
                   	}

316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
                    if (red+green+blue > threshold)
                    {
                        rfg += red;
                        gfg += green;
                        bfg += blue;
                        fgBits++;
                        pXorBits[xorIndex] |= bitShifted;
                    }
                    else
                    {
                        rbg += red;
                        gbg += green;
                        bbg += blue;
                    }
                    if (x%8 == 7)
                    {
                        bitShifted = 0x01;
                        xorIndex++;
                    }
                    else
                        bitShifted = bitShifted << 1;
                }
            }
            rscale = 1 << (16 - rbits);
            gscale = 1 << (16 - gbits);
            bscale = 1 << (16 - bbits);
342 343 344 345 346 347 348
            if (fgBits)
            {
                fg.red   = rfg * rscale / fgBits;
                fg.green = gfg * gscale / fgBits;
                fg.blue  = bfg * bscale / fgBits;
            }
            else fg.red = fg.green = fg.blue = 0;
349
            bgBits = xmax * ymax - fgBits;
350 351 352 353 354 355 356
            if (bgBits)
            {
                bg.red   = rbg * rscale / bgBits;
                bg.green = gbg * gscale / bgBits;
                bg.blue  = bbg * bscale / bgBits;
            }
            else bg.red = bg.green = bg.blue = 0;
357 358 359 360 361 362 363 364 365
            pixmapBits = XCreateBitmapFromData( display, root_window, pXorBits, xmax, ymax );
            if (!pixmapBits)
            {
                XFreePixmap( display, pixmapAll );
                XFreeGC( display, gc );
                image->data = NULL;
                XDestroyImage( image );
                return 0;
            }
366

367 368 369 370 371 372 373 374 375
            /* Put the mask. */
            XPutImage( display, pixmapAll, gc, image,
                   0, 0, 0, 0, ptr->nWidth, ptr->nHeight );
            XSetFunction( display, gc, GXcopy );
            /* Put the image */
            XCopyArea( display, pixmapBits, pixmapAll, gc,
                       0, 0, xmax, ymax, 0, ptr->nHeight );
            XFreePixmap( display, pixmapBits );
        }
376 377
        image->data = NULL;
        XDestroyImage( image );
378 379 380

        /* Now create the 2 pixmaps for bits and mask */

381 382 383
        pixmapBits = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
        pixmapMask = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
        pixmapMaskInv = XCreatePixmap( display, root_window, ptr->nWidth, ptr->nHeight, 1 );
384 385 386

        /* Make sure everything went OK so far */

387
        if (pixmapBits && pixmapMask && pixmapMaskInv)
388
        {
389 390
            POINT hotspot;

391 392 393 394
            /* We have to do some magic here, as cursors are not fully
             * compatible between Windows and X11. Under X11, there
             * are only 3 possible color cursor: black, white and
             * masked. So we map the 4th Windows color (invert the
395
             * bits on the screen) to black and an additional white bit on
396
             * an other place (+1,+1). This require some boolean arithmetic:
397 398
             *
             *         Windows          |          X11
399
             * And    Xor      Result   |   Bits     Mask     Result
400
             *  0      0     black      |    0        1     background
401 402
             *  0      1     white      |    1        1     foreground
             *  1      0     no change  |    X        0     no change
403 404 405
             *  1      1     inverted   |    0        1     background
             *
             * which gives:
406 407
             *  Bits = not 'And' and 'Xor' or 'And2' and 'Xor2'
             *  Mask = not 'And' or 'Xor' or 'And2' and 'Xor2'
408 409 410 411 412
             *
             * FIXME: apparently some servers do support 'inverted' color.
             * I don't know if it's correct per the X spec, but maybe
             * we ought to take advantage of it.  -- AJ
             */
413 414
            XSetFunction( display, gc, GXcopy );
            XCopyArea( display, pixmapAll, pixmapBits, gc,
415
                       0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
416
            XCopyArea( display, pixmapAll, pixmapMask, gc,
417
                       0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
418
            XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
419
                       0, 0, ptr->nWidth, ptr->nHeight, 0, 0 );
420 421
            XSetFunction( display, gc, GXand );
            XCopyArea( display, pixmapAll, pixmapMaskInv, gc,
422
                       0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
423 424
            XSetFunction( display, gc, GXandReverse );
            XCopyArea( display, pixmapAll, pixmapBits, gc,
425
                       0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
426 427
            XSetFunction( display, gc, GXorReverse );
            XCopyArea( display, pixmapAll, pixmapMask, gc,
428
                       0, ptr->nHeight, ptr->nWidth, ptr->nHeight, 0, 0 );
429
            /* Additional white */
430 431
            XSetFunction( display, gc, GXor );
            XCopyArea( display, pixmapMaskInv, pixmapMask, gc,
432
                       0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
433
            XCopyArea( display, pixmapMaskInv, pixmapBits, gc,
434
                       0, 0, ptr->nWidth, ptr->nHeight, 1, 1 );
435
            XSetFunction( display, gc, GXcopy );
436 437 438 439 440 441 442 443 444 445

            /* Make sure hotspot is valid */
            hotspot.x = ptr->ptHotSpot.x;
            hotspot.y = ptr->ptHotSpot.y;
            if (hotspot.x < 0 || hotspot.x >= ptr->nWidth ||
                hotspot.y < 0 || hotspot.y >= ptr->nHeight)
            {
                hotspot.x = ptr->nWidth / 2;
                hotspot.y = ptr->nHeight / 2;
            }
446
            cursor = XCreatePixmapCursor( display, pixmapBits, pixmapMask,
447
                                          &fg, &bg, hotspot.x, hotspot.y );
448 449 450 451 452 453 454
        }

        /* Now free everything */

        if (pixmapAll) XFreePixmap( display, pixmapAll );
        if (pixmapBits) XFreePixmap( display, pixmapBits );
        if (pixmapMask) XFreePixmap( display, pixmapMask );
455
        if (pixmapMaskInv) XFreePixmap( display, pixmapMaskInv );
456
        XFreeGC( display, gc );
457
    }
458 459
    return cursor;
}
460

461 462

/***********************************************************************
463
 *		SetCursor (X11DRV.@)
464
 */
465
void X11DRV_SetCursor( CURSORICONINFO *lpCursor )
466
{
467
    Cursor cursor;
468

469
    if (root_window != DefaultRootWindow(gdi_display))
470
    {
471 472
        /* If in desktop mode, set the cursor on the desktop window */

473
        wine_tsx11_lock();
474
        cursor = create_cursor( gdi_display, lpCursor );
475 476 477
        if (cursor)
        {
            XDefineCursor( gdi_display, root_window, cursor );
478 479
	    /* Make the change take effect immediately */
	    XFlush(gdi_display);
480 481 482
            XFreeCursor( gdi_display, cursor );
        }
        wine_tsx11_unlock();
483
    }
484
    else /* set the same cursor for all top-level windows of the current thread */
485
    {
486
        struct x11drv_thread_data *data = x11drv_thread_data();
487

488
        wine_tsx11_lock();
489
        cursor = create_cursor( data->display, lpCursor );
490
        if (cursor)
491
        {
492 493 494 495 496 497 498 499
            if (data->cursor) XFreeCursor( data->display, data->cursor );
            data->cursor = cursor;
            if (data->cursor_window)
            {
                XDefineCursor( data->display, data->cursor_window, cursor );
                /* Make the change take effect immediately */
                XFlush( data->display );
            }
500
        }
501
        wine_tsx11_unlock();
502 503 504 505
    }
}

/***********************************************************************
506
 *		SetCursorPos (X11DRV.@)
507
 */
508
void X11DRV_SetCursorPos( INT x, INT y )
509
{
510
    Display *display = thread_display();
511

512
    TRACE( "warping to (%d,%d)\n", x, y );
513

514 515 516 517
    wine_tsx11_lock();
    XWarpPointer( display, root_window, root_window, 0, 0, 0, 0, x, y );
    XFlush( display ); /* just in case */
    wine_tsx11_unlock();
518 519 520 521 522 523 524 525 526 527 528 529
}

/***********************************************************************
 *		GetCursorPos (X11DRV.@)
 */
void X11DRV_GetCursorPos(LPPOINT pos)
{
  Display *display = thread_display();
  Window root, child;
  int rootX, rootY, winX, winY;
  unsigned int xstate;

530 531 532 533 534 535 536 537 538 539 540
  wine_tsx11_lock();
  if (XQueryPointer( display, root_window, &root, &child,
                     &rootX, &rootY, &winX, &winY, &xstate ))
  {
      update_key_state( xstate );
      update_button_state( xstate );
      TRACE("pointer at (%d,%d)\n", winX, winY );
      pos->x = winX;
      pos->y = winY;
  }
  wine_tsx11_unlock();
541 542
}

543
/***********************************************************************
544
 *		InitMouse (X11DRV.@)
545
 */
546 547 548 549 550 551 552 553 554 555
void X11DRV_InitMouse( BYTE *key_state_table )
{
    pKeyStateTable = key_state_table;
}


/***********************************************************************
 *           X11DRV_ButtonPress
 */
void X11DRV_ButtonPress( HWND hwnd, XButtonEvent *event )
556
{
557 558 559
    int buttonNum = event->button - 1;
    WORD wData = 0;
    POINT pt;
560

561
    if (buttonNum >= NB_BUTTONS) return;
562
    if (!hwnd) return;
563

564
    update_cursor( hwnd, event->window );
565 566 567
    get_coords( &hwnd, event->window, event->x, event->y, &pt );

    switch (buttonNum)
568
    {
569 570 571 572 573 574
    case 3:
        wData = WHEEL_DELTA;
        break;
    case 4:
        wData = -WHEEL_DELTA;
        break;
575
    }
576 577 578
    update_key_state( event->state );
    send_mouse_event( hwnd, button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE,
                      pt.x, pt.y, wData, event->time );
579 580
}

581

582
/***********************************************************************
583
 *           X11DRV_ButtonRelease
584
 */
585
void X11DRV_ButtonRelease( HWND hwnd, XButtonEvent *event )
586
{
587 588
    int buttonNum = event->button - 1;
    POINT pt;
589

590
    if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return;
591
    if (!hwnd) return;
592

593
    update_cursor( hwnd, event->window );
594 595 596 597 598
    get_coords( &hwnd, event->window, event->x, event->y, &pt );
    update_key_state( event->state );
    send_mouse_event( hwnd, button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE,
                      pt.x, pt.y, 0, event->time );
}
599 600


601 602 603 604 605 606 607
/***********************************************************************
 *           X11DRV_MotionNotify
 */
void X11DRV_MotionNotify( HWND hwnd, XMotionEvent *event )
{
    POINT pt;

608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
    if (!hwnd) return;

    update_cursor( hwnd, event->window );
    get_coords( &hwnd, event->window, event->x, event->y, &pt );
    update_key_state( event->state );
    send_mouse_event( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
                      pt.x, pt.y, 0, event->time );
}


/***********************************************************************
 *           X11DRV_EnterNotify
 */
void X11DRV_EnterNotify( HWND hwnd, XCrossingEvent *event )
{
    POINT pt;

    if (event->detail == NotifyVirtual || event->detail == NotifyNonlinearVirtual) return;
    if (!hwnd) return;

    /* simulate a mouse motion event */
    update_cursor( hwnd, event->window );
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
    get_coords( &hwnd, event->window, event->x, event->y, &pt );
    update_key_state( event->state );
    send_mouse_event( hwnd, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
                      pt.x, pt.y, 0, event->time );
}


#ifdef HAVE_LIBXXF86DGA2
/**********************************************************************
 *              X11DRV_DGAMotionEvent
 */
void X11DRV_DGAMotionEvent( HWND hwnd, XDGAMotionEvent *event )
{
    update_key_state( event->state );
    send_mouse_event( hwnd, MOUSEEVENTF_MOVE, event->dx, event->dy, 0, event->time );
}

/**********************************************************************
 *              X11DRV_DGAButtonPressEvent
 */
void X11DRV_DGAButtonPressEvent( HWND hwnd, XDGAButtonEvent *event )
{
    int buttonNum = event->button - 1;

    if (buttonNum >= NB_BUTTONS) return;
    update_key_state( event->state );
    send_mouse_event( hwnd, button_down_flags[buttonNum], 0, 0, 0, event->time );
}

/**********************************************************************
 *              X11DRV_DGAButtonReleaseEvent
 */
void X11DRV_DGAButtonReleaseEvent( HWND hwnd, XDGAButtonEvent *event )
{
    int buttonNum = event->button - 1;

    if (buttonNum >= NB_BUTTONS) return;
    update_key_state( event->state );
    send_mouse_event( hwnd, button_up_flags[buttonNum], 0, 0, 0, event->time );
669
}
670
#endif /* HAVE_LIBXXF86DGA2 */