src/video/x11/SDL_x11events.c
author Sam Lantinga
Sun, 20 Oct 2013 20:42:55 -0700
changeset 7857 6388f5229bb7
parent 7839 d2804f8153fe
child 7885 a8bd63b33636
permissions -rw-r--r--
Added a macro SDL_TICKS_PASSED() to correctly compare two 32-bit tick values.
Went through the code and used the macro and fixed a couple places that were using incorrect timestamp comparisons.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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_config.h"
    22 
    23 #if SDL_VIDEO_DRIVER_X11
    24 
    25 #include <sys/types.h>
    26 #include <sys/time.h>
    27 #include <signal.h>
    28 #include <unistd.h>
    29 #include <limits.h> /* For INT_MAX */
    30 
    31 #include "SDL_x11video.h"
    32 #include "SDL_x11video.h"
    33 #include "SDL_x11touch.h"
    34 #include "SDL_x11xinput2.h"
    35 #include "../../events/SDL_events_c.h"
    36 #include "../../events/SDL_mouse_c.h"
    37 #include "../../events/SDL_touch_c.h"
    38 
    39 #include "SDL_timer.h"
    40 #include "SDL_syswm.h"
    41 
    42 #include <stdio.h>
    43 
    44 typedef struct {
    45     unsigned char *data;
    46     int format, count;
    47     Atom type;
    48 } SDL_x11Prop;
    49 
    50 /* Reads property
    51    Must call X11_XFree on results
    52  */
    53 static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop)
    54 {
    55     unsigned char *ret=NULL;
    56     Atom type;
    57     int fmt;
    58     unsigned long count;
    59     unsigned long bytes_left;
    60     int bytes_fetch = 0;
    61 
    62     do {
    63         if (ret != 0) X11_XFree(ret);
    64         X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret);
    65         bytes_fetch += bytes_left;
    66     } while (bytes_left != 0);
    67 
    68     p->data=ret;
    69     p->format=fmt;
    70     p->count=count;
    71     p->type=type;
    72 }
    73 
    74 /* Find text-uri-list in a list of targets and return it's atom
    75    if available, else return None */
    76 static Atom X11_PickTarget(Display *disp, Atom list[], int list_count)
    77 {
    78     Atom request = None;
    79     char *name;
    80     int i;
    81     for (i=0; i < list_count && request == None; i++) {
    82         name = X11_XGetAtomName(disp, list[i]);
    83         if (strcmp("text/uri-list", name)==0) request = list[i];
    84         X11_XFree(name);
    85     }
    86     return request;
    87 }
    88 
    89 /* Wrapper for X11_PickTarget for a maximum of three targets, a special
    90    case in the Xdnd protocol */
    91 static Atom X11_PickTargetFromAtoms(Display *disp, Atom a0, Atom a1, Atom a2)
    92 {
    93     int count=0;
    94     Atom atom[3];
    95     if (a0 != None) atom[count++] = a0;
    96     if (a1 != None) atom[count++] = a1;
    97     if (a2 != None) atom[count++] = a2;
    98     return X11_PickTarget(disp, atom, count);
    99 }
   100 /* #define DEBUG_XEVENTS */
   101 
   102 struct KeyRepeatCheckData
   103 {
   104     XEvent *event;
   105     SDL_bool found;
   106 };
   107 
   108 static Bool X11_KeyRepeatCheckIfEvent(Display *display, XEvent *chkev,
   109     XPointer arg)
   110 {
   111     struct KeyRepeatCheckData *d = (struct KeyRepeatCheckData *) arg;
   112     if (chkev->type == KeyPress &&
   113         chkev->xkey.keycode == d->event->xkey.keycode &&
   114         chkev->xkey.time - d->event->xkey.time < 2)
   115         d->found = SDL_TRUE;
   116     return False;
   117 }
   118 
   119 /* Check to see if this is a repeated key.
   120    (idea shamelessly lifted from GII -- thanks guys! :)
   121  */
   122 static SDL_bool X11_KeyRepeat(Display *display, XEvent *event)
   123 {
   124     XEvent dummyev;
   125     struct KeyRepeatCheckData d;
   126     d.event = event;
   127     d.found = SDL_FALSE;
   128     if (X11_XPending(display))
   129         X11_XCheckIfEvent(display, &dummyev, X11_KeyRepeatCheckIfEvent,
   130             (XPointer) &d);
   131     return d.found;
   132 }
   133 
   134 static Bool X11_IsWheelCheckIfEvent(Display *display, XEvent *chkev,
   135     XPointer arg)
   136 {
   137     XEvent *event = (XEvent *) arg;
   138     /* we only handle buttons 4 and 5 - false positive avoidance */
   139     if (chkev->type == ButtonRelease &&
   140         (event->xbutton.button == Button4 || event->xbutton.button == Button5) &&
   141         chkev->xbutton.button == event->xbutton.button &&
   142         chkev->xbutton.time == event->xbutton.time)
   143         return True;
   144     return False;
   145 }
   146 
   147 static SDL_bool X11_IsWheelEvent(Display * display,XEvent * event,int * ticks)
   148 {
   149     XEvent relevent;
   150     if (X11_XPending(display)) {
   151         /* according to the xlib docs, no specific mouse wheel events exist.
   152            however, mouse wheel events trigger a button press and a button release
   153            immediately. thus, checking if the same button was released at the same
   154            time as it was pressed, should be an adequate hack to derive a mouse
   155            wheel event.
   156            However, there is broken and unusual hardware out there...
   157            - False positive: a button for which a release event is
   158              generated (or synthesised) immediately.
   159            - False negative: a wheel which, when rolled, doesn't have
   160              a release event generated immediately. */
   161         if (X11_XCheckIfEvent(display, &relevent, X11_IsWheelCheckIfEvent,
   162             (XPointer) event)) {
   163 
   164             /* by default, X11 only knows 5 buttons. on most 3 button + wheel mouse,
   165                Button4 maps to wheel up, Button5 maps to wheel down. */
   166             if (event->xbutton.button == Button4) {
   167                 *ticks = 1;
   168             }
   169             else if (event->xbutton.button == Button5) {
   170                 *ticks = -1;
   171             }
   172             return SDL_TRUE;
   173         }
   174     }
   175     return SDL_FALSE;
   176 }
   177 
   178 /* Convert URI to local filename
   179    return filename if possible, else NULL
   180 */
   181 static char* X11_URIToLocal(char* uri) {
   182     char *file = NULL;
   183     SDL_bool local;
   184 
   185     if (memcmp(uri,"file:/",6) == 0) uri += 6;      /* local file? */
   186     else if (strstr(uri,":/") != NULL) return file; /* wrong scheme */
   187 
   188     local = uri[0] != '/' || ( uri[0] != '\0' && uri[1] == '/' );
   189 
   190     /* got a hostname? */
   191     if ( !local && uri[0] == '/' && uri[2] != '/' ) {
   192       char* hostname_end = strchr( uri+1, '/' );
   193       if ( hostname_end != NULL ) {
   194           char hostname[ 257 ];
   195           if ( gethostname( hostname, 255 ) == 0 ) {
   196             hostname[ 256 ] = '\0';
   197             if ( memcmp( uri+1, hostname, hostname_end - ( uri+1 )) == 0 ) {
   198                 uri = hostname_end + 1;
   199                 local = SDL_TRUE;
   200             }
   201           }
   202       }
   203     }
   204     if ( local ) {
   205       file = uri;
   206       if ( uri[1] == '/' ) {
   207           file++;
   208       } else {
   209           file--;
   210       }
   211     }
   212     return file;
   213 }
   214 
   215 #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
   216 static void X11_HandleGenericEvent(SDL_VideoData *videodata,XEvent event)
   217 {
   218     /* event is a union, so cookie == &event, but this is type safe. */
   219     XGenericEventCookie *cookie = &event.xcookie;
   220     if (X11_XGetEventData(videodata->display, cookie)) {
   221         X11_HandleXinput2Event(videodata, cookie);
   222         X11_XFreeEventData(videodata->display, cookie);
   223     }
   224 }
   225 #endif /* SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS */
   226 
   227 
   228 static void
   229 X11_DispatchFocusIn(SDL_WindowData *data)
   230 {
   231 #ifdef DEBUG_XEVENTS
   232     printf("window %p: Dispatching FocusIn\n", data);
   233 #endif
   234     SDL_SetKeyboardFocus(data->window);
   235 #ifdef X_HAVE_UTF8_STRING
   236     if (data->ic) {
   237         X11_XSetICFocus(data->ic);
   238     }
   239 #endif
   240 }
   241 
   242 static void
   243 X11_DispatchFocusOut(SDL_WindowData *data)
   244 {
   245 #ifdef DEBUG_XEVENTS
   246     printf("window %p: Dispatching FocusOut\n", data);
   247 #endif
   248     SDL_SetKeyboardFocus(NULL);
   249 #ifdef X_HAVE_UTF8_STRING
   250     if (data->ic) {
   251         X11_XUnsetICFocus(data->ic);
   252     }
   253 #endif
   254 }
   255 
   256 static void
   257 X11_DispatchMapNotify(SDL_WindowData *data)
   258 {
   259     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   260     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
   261 }
   262 
   263 static void
   264 X11_DispatchUnmapNotify(SDL_WindowData *data)
   265 {
   266     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   267     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   268 }
   269 
   270 static void
   271 X11_DispatchEvent(_THIS)
   272 {
   273     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   274     Display *display = videodata->display;
   275     SDL_WindowData *data;
   276     XEvent xevent;
   277     int i;
   278     XClientMessageEvent m;
   279 
   280     SDL_zero(xevent);           /* valgrind fix. --ryan. */
   281     X11_XNextEvent(display, &xevent);
   282 
   283     /* filter events catchs XIM events and sends them to the correct
   284        handler */
   285     if (X11_XFilterEvent(&xevent, None) == True) {
   286 #if 0
   287         printf("Filtered event type = %d display = %d window = %d\n",
   288                xevent.type, xevent.xany.display, xevent.xany.window);
   289 #endif
   290         return;
   291     }
   292 
   293     /* Send a SDL_SYSWMEVENT if the application wants them */
   294     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   295         SDL_SysWMmsg wmmsg;
   296 
   297         SDL_VERSION(&wmmsg.version);
   298         wmmsg.subsystem = SDL_SYSWM_X11;
   299         wmmsg.msg.x11.event = xevent;
   300         SDL_SendSysWMEvent(&wmmsg);
   301     }
   302 
   303 #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
   304     if(xevent.type == GenericEvent) {
   305         X11_HandleGenericEvent(videodata,xevent);
   306         return;
   307     }
   308 #endif
   309 
   310 #if 0
   311     printf("type = %d display = %d window = %d\n",
   312            xevent.type, xevent.xany.display, xevent.xany.window);
   313 #endif
   314 
   315     data = NULL;
   316     if (videodata && videodata->windowlist) {
   317         for (i = 0; i < videodata->numwindows; ++i) {
   318             if ((videodata->windowlist[i] != NULL) &&
   319                 (videodata->windowlist[i]->xwindow == xevent.xany.window)) {
   320                 data = videodata->windowlist[i];
   321                 break;
   322             }
   323         }
   324     }
   325     if (!data) {
   326         return;
   327     }
   328 
   329     switch (xevent.type) {
   330 
   331         /* Gaining mouse coverage? */
   332     case EnterNotify:{
   333 #ifdef DEBUG_XEVENTS
   334             printf("window %p: EnterNotify! (%d,%d,%d)\n", data,
   335                    xevent.xcrossing.x,
   336                    xevent.xcrossing.y,
   337                    xevent.xcrossing.mode);
   338             if (xevent.xcrossing.mode == NotifyGrab)
   339                 printf("Mode: NotifyGrab\n");
   340             if (xevent.xcrossing.mode == NotifyUngrab)
   341                 printf("Mode: NotifyUngrab\n");
   342 #endif
   343             SDL_SetMouseFocus(data->window);
   344 
   345             if (!SDL_GetMouse()->relative_mode) {
   346                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y);
   347             }
   348         }
   349         break;
   350         /* Losing mouse coverage? */
   351     case LeaveNotify:{
   352 #ifdef DEBUG_XEVENTS
   353             printf("window %p: LeaveNotify! (%d,%d,%d)\n", data,
   354                    xevent.xcrossing.x,
   355                    xevent.xcrossing.y,
   356                    xevent.xcrossing.mode);
   357             if (xevent.xcrossing.mode == NotifyGrab)
   358                 printf("Mode: NotifyGrab\n");
   359             if (xevent.xcrossing.mode == NotifyUngrab)
   360                 printf("Mode: NotifyUngrab\n");
   361 #endif
   362             if (!SDL_GetMouse()->relative_mode) {
   363                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y);
   364             }
   365 
   366             if (xevent.xcrossing.mode != NotifyGrab &&
   367                 xevent.xcrossing.mode != NotifyUngrab &&
   368                 xevent.xcrossing.detail != NotifyInferior) {
   369                 SDL_SetMouseFocus(NULL);
   370             }
   371         }
   372         break;
   373 
   374         /* Gaining input focus? */
   375     case FocusIn:{
   376             if (xevent.xfocus.detail == NotifyInferior) {
   377 #ifdef DEBUG_XEVENTS
   378                 printf("window %p: FocusIn (NotifierInferior, ignoring)\n", data);
   379 #endif
   380                 break;
   381             }
   382 #ifdef DEBUG_XEVENTS
   383             printf("window %p: FocusIn!\n", data);
   384 #endif
   385             if (data->pending_focus == PENDING_FOCUS_OUT &&
   386                 data->window == SDL_GetKeyboardFocus()) {
   387                 /* We want to reset the keyboard here, because we may have
   388                    missed keyboard messages after our previous FocusOut.
   389                  */
   390                 /* Actually, if we do this we clear the ALT key on Unity
   391                    because it briefly takes focus for their dashboard.
   392 
   393                    I think it's better to think the ALT key is held down
   394                    when it's not, then always lose the ALT modifier on Unity.
   395                  */
   396                 /* SDL_ResetKeyboard(); */
   397             }
   398             data->pending_focus = PENDING_FOCUS_IN;
   399             data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_IN_TIME;
   400         }
   401         break;
   402 
   403         /* Losing input focus? */
   404     case FocusOut:{
   405             if (xevent.xfocus.detail == NotifyInferior) {
   406                 /* We still have focus if a child gets focus */
   407 #ifdef DEBUG_XEVENTS
   408                 printf("window %p: FocusOut (NotifierInferior, ignoring)\n", data);
   409 #endif
   410                 break;
   411             }
   412 #ifdef DEBUG_XEVENTS
   413             printf("window %p: FocusOut!\n", data);
   414 #endif
   415             data->pending_focus = PENDING_FOCUS_OUT;
   416             data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_OUT_TIME;
   417         }
   418         break;
   419 
   420         /* Generated upon EnterWindow and FocusIn */
   421     case KeymapNotify:{
   422 #ifdef DEBUG_XEVENTS
   423             printf("window %p: KeymapNotify!\n", data);
   424 #endif
   425             /* FIXME:
   426                X11_SetKeyboardState(SDL_Display, xevent.xkeymap.key_vector);
   427              */
   428         }
   429         break;
   430 
   431         /* Has the keyboard layout changed? */
   432     case MappingNotify:{
   433 #ifdef DEBUG_XEVENTS
   434             printf("window %p: MappingNotify!\n", data);
   435 #endif
   436             X11_UpdateKeymap(_this);
   437         }
   438         break;
   439 
   440         /* Key press? */
   441     case KeyPress:{
   442             KeyCode keycode = xevent.xkey.keycode;
   443             KeySym keysym = NoSymbol;
   444             char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
   445             Status status = 0;
   446 
   447 #ifdef DEBUG_XEVENTS
   448             printf("window %p: KeyPress (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode);
   449 #endif
   450             SDL_SendKeyboardKey(SDL_PRESSED, videodata->key_layout[keycode]);
   451 #if 1
   452             if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) {
   453                 int min_keycode, max_keycode;
   454                 X11_XDisplayKeycodes(display, &min_keycode, &max_keycode);
   455 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
   456                 keysym = X11_XkbKeycodeToKeysym(display, keycode, 0, 0);
   457 #else
   458                 keysym = XKeycodeToKeysym(display, keycode, 0);
   459 #endif
   460                 fprintf(stderr,
   461                         "The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL mailing list <sdl@libsdl.org> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).\n",
   462                         keycode, keycode - min_keycode, keysym,
   463                         X11_XKeysymToString(keysym));
   464             }
   465 #endif
   466             /* */
   467             SDL_zero(text);
   468 #ifdef X_HAVE_UTF8_STRING
   469             if (data->ic) {
   470                 X11_Xutf8LookupString(data->ic, &xevent.xkey, text, sizeof(text),
   471                                   &keysym, &status);
   472             }
   473 #else
   474             XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL);
   475 #endif
   476             if (*text) {
   477                 SDL_SendKeyboardText(text);
   478             }
   479         }
   480         break;
   481 
   482         /* Key release? */
   483     case KeyRelease:{
   484             KeyCode keycode = xevent.xkey.keycode;
   485 
   486 #ifdef DEBUG_XEVENTS
   487             printf("window %p: KeyRelease (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode);
   488 #endif
   489             if (X11_KeyRepeat(display, &xevent)) {
   490                 /* We're about to get a repeated key down, ignore the key up */
   491                 break;
   492             }
   493             SDL_SendKeyboardKey(SDL_RELEASED, videodata->key_layout[keycode]);
   494         }
   495         break;
   496 
   497         /* Have we been iconified? */
   498     case UnmapNotify:{
   499 #ifdef DEBUG_XEVENTS
   500             printf("window %p: UnmapNotify!\n", data);
   501 #endif
   502             X11_DispatchUnmapNotify(data);
   503         }
   504         break;
   505 
   506         /* Have we been restored? */
   507     case MapNotify:{
   508 #ifdef DEBUG_XEVENTS
   509             printf("window %p: MapNotify!\n", data);
   510 #endif
   511             X11_DispatchMapNotify(data);
   512         }
   513         break;
   514 
   515         /* Have we been resized or moved? */
   516     case ConfigureNotify:{
   517 #ifdef DEBUG_XEVENTS
   518             printf("window %p: ConfigureNotify! (position: %d,%d, size: %dx%d)\n", data,
   519                    xevent.xconfigure.x, xevent.xconfigure.y,
   520                    xevent.xconfigure.width, xevent.xconfigure.height);
   521 #endif
   522             long border_left = 0;
   523             long border_right = 0;
   524             long border_top = 0;
   525             long border_bottom = 0;
   526             if (data->xwindow) {
   527                 Atom _net_frame_extents = X11_XInternAtom(display, "_NET_FRAME_EXTENTS", 0);
   528                 Atom type;
   529                 int format;
   530                 unsigned long nitems, bytes_after;
   531                 unsigned char *property;
   532                 X11_XGetWindowProperty(display, data->xwindow,
   533                     _net_frame_extents, 0, 16, 0,
   534                     XA_CARDINAL, &type, &format,
   535                     &nitems, &bytes_after, &property);
   536 
   537                 border_left = ((long*)property)[0];
   538                 border_right = ((long*)property)[1];
   539                 border_top = ((long*)property)[2];
   540                 border_bottom = ((long*)property)[3];
   541             }
   542 
   543             if (xevent.xconfigure.x != data->last_xconfigure.x ||
   544                 xevent.xconfigure.y != data->last_xconfigure.y) {
   545                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED,
   546                                     xevent.xconfigure.x - border_left,
   547                                     xevent.xconfigure.y - border_top);
   548             }
   549             if (xevent.xconfigure.width != data->last_xconfigure.width ||
   550                 xevent.xconfigure.height != data->last_xconfigure.height) {
   551                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED,
   552                                     xevent.xconfigure.width,
   553                                     xevent.xconfigure.height);
   554             }
   555             data->last_xconfigure = xevent.xconfigure;
   556         }
   557         break;
   558 
   559         /* Have we been requested to quit (or another client message?) */
   560     case ClientMessage:{
   561 
   562             int xdnd_version=0;
   563 
   564             if (xevent.xclient.message_type == videodata->XdndEnter) {
   565                 SDL_bool use_list = xevent.xclient.data.l[1] & 1;
   566                 data->xdnd_source = xevent.xclient.data.l[0];
   567                 xdnd_version = ( xevent.xclient.data.l[1] >> 24);
   568                 if (use_list) {
   569                     /* fetch conversion targets */
   570                     SDL_x11Prop p;
   571                     X11_ReadProperty(&p, display, data->xdnd_source, videodata->XdndTypeList);
   572                     /* pick one */
   573                     data->xdnd_req = X11_PickTarget(display, (Atom*)p.data, p.count);
   574                     X11_XFree(p.data);
   575                 } else {
   576                     /* pick from list of three */
   577                     data->xdnd_req = X11_PickTargetFromAtoms(display, xevent.xclient.data.l[2], xevent.xclient.data.l[3], xevent.xclient.data.l[4]);
   578                 }
   579             }
   580             else if (xevent.xclient.message_type == videodata->XdndPosition) {
   581 
   582                 /* reply with status */
   583                 memset(&m, 0, sizeof(XClientMessageEvent));
   584                 m.type = ClientMessage;
   585                 m.display = xevent.xclient.display;
   586                 m.window = xevent.xclient.data.l[0];
   587                 m.message_type = videodata->XdndStatus;
   588                 m.format=32;
   589                 m.data.l[0] = data->xwindow;
   590                 m.data.l[1] = (data->xdnd_req != None);
   591                 m.data.l[2] = 0; /* specify an empty rectangle */
   592                 m.data.l[3] = 0;
   593                 m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */
   594 
   595                 X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
   596                 X11_XFlush(display);
   597             }
   598             else if(xevent.xclient.message_type == videodata->XdndDrop) {
   599                 if (data->xdnd_req == None) {
   600                     /* say again - not interested! */
   601                     memset(&m, 0, sizeof(XClientMessageEvent));
   602                     m.type = ClientMessage;
   603                     m.display = xevent.xclient.display;
   604                     m.window = xevent.xclient.data.l[0];
   605                     m.message_type = videodata->XdndFinished;
   606                     m.format=32;
   607                     m.data.l[0] = data->xwindow;
   608                     m.data.l[1] = 0;
   609                     m.data.l[2] = None; /* fail! */
   610                     X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
   611                 } else {
   612                     /* convert */
   613                     if(xdnd_version >= 1) {
   614                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent.xclient.data.l[2]);
   615                     } else {
   616                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime);
   617                     }
   618                 }
   619             }
   620             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
   621                 (xevent.xclient.format == 32) &&
   622                 (xevent.xclient.data.l[0] == videodata->_NET_WM_PING)) {
   623                 Window root = DefaultRootWindow(display);
   624 
   625 #ifdef DEBUG_XEVENTS
   626                 printf("window %p: _NET_WM_PING\n", data);
   627 #endif
   628                 xevent.xclient.window = root;
   629                 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xevent);
   630                 break;
   631             }
   632 
   633             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
   634                 (xevent.xclient.format == 32) &&
   635                 (xevent.xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) {
   636 
   637 #ifdef DEBUG_XEVENTS
   638                 printf("window %p: WM_DELETE_WINDOW\n", data);
   639 #endif
   640                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   641                 break;
   642             }
   643         }
   644         break;
   645 
   646         /* Do we need to refresh ourselves? */
   647     case Expose:{
   648 #ifdef DEBUG_XEVENTS
   649             printf("window %p: Expose (count = %d)\n", data, xevent.xexpose.count);
   650 #endif
   651             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
   652         }
   653         break;
   654 
   655     case MotionNotify:{
   656             SDL_Mouse *mouse = SDL_GetMouse();
   657             if(!mouse->relative_mode) {
   658 #ifdef DEBUG_MOTION
   659                 printf("window %p: X11 motion: %d,%d\n", xevent.xmotion.x, xevent.xmotion.y);
   660 #endif
   661 
   662                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xmotion.x, xevent.xmotion.y);
   663             }
   664         }
   665         break;
   666 
   667     case ButtonPress:{
   668             int ticks = 0;
   669             if (X11_IsWheelEvent(display,&xevent,&ticks)) {
   670                 SDL_SendMouseWheel(data->window, 0, 0, ticks);
   671             } else {
   672                 SDL_SendMouseButton(data->window, 0, SDL_PRESSED, xevent.xbutton.button);
   673             }
   674         }
   675         break;
   676 
   677     case ButtonRelease:{
   678             SDL_SendMouseButton(data->window, 0, SDL_RELEASED, xevent.xbutton.button);
   679         }
   680         break;
   681 
   682     case PropertyNotify:{
   683 #ifdef DEBUG_XEVENTS
   684             unsigned char *propdata;
   685             int status, real_format;
   686             Atom real_type;
   687             unsigned long items_read, items_left, i;
   688 
   689             char *name = X11_XGetAtomName(display, xevent.xproperty.atom);
   690             if (name) {
   691                 printf("window %p: PropertyNotify: %s %s\n", data, name, (xevent.xproperty.state == PropertyDelete) ? "deleted" : "changed");
   692                 X11_XFree(name);
   693             }
   694 
   695             status = X11_XGetWindowProperty(display, data->xwindow, xevent.xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata);
   696             if (status == Success && items_read > 0) {
   697                 if (real_type == XA_INTEGER) {
   698                     int *values = (int *)propdata;
   699 
   700                     printf("{");
   701                     for (i = 0; i < items_read; i++) {
   702                         printf(" %d", values[i]);
   703                     }
   704                     printf(" }\n");
   705                 } else if (real_type == XA_CARDINAL) {
   706                     if (real_format == 32) {
   707                         Uint32 *values = (Uint32 *)propdata;
   708 
   709                         printf("{");
   710                         for (i = 0; i < items_read; i++) {
   711                             printf(" %d", values[i]);
   712                         }
   713                         printf(" }\n");
   714                     } else if (real_format == 16) {
   715                         Uint16 *values = (Uint16 *)propdata;
   716 
   717                         printf("{");
   718                         for (i = 0; i < items_read; i++) {
   719                             printf(" %d", values[i]);
   720                         }
   721                         printf(" }\n");
   722                     } else if (real_format == 8) {
   723                         Uint8 *values = (Uint8 *)propdata;
   724 
   725                         printf("{");
   726                         for (i = 0; i < items_read; i++) {
   727                             printf(" %d", values[i]);
   728                         }
   729                         printf(" }\n");
   730                     }
   731                 } else if (real_type == XA_STRING ||
   732                            real_type == videodata->UTF8_STRING) {
   733                     printf("{ \"%s\" }\n", propdata);
   734                 } else if (real_type == XA_ATOM) {
   735                     Atom *atoms = (Atom *)propdata;
   736 
   737                     printf("{");
   738                     for (i = 0; i < items_read; i++) {
   739                         char *name = X11_XGetAtomName(display, atoms[i]);
   740                         if (name) {
   741                             printf(" %s", name);
   742                             X11_XFree(name);
   743                         }
   744                     }
   745                     printf(" }\n");
   746                 } else {
   747                     char *name = X11_XGetAtomName(display, real_type);
   748                     printf("Unknown type: %ld (%s)\n", real_type, name ? name : "UNKNOWN");
   749                     if (name) {
   750                         X11_XFree(name);
   751                     }
   752                 }
   753             }
   754             if (status == Success) {
   755                 X11_XFree(propdata);
   756             }
   757 #endif /* DEBUG_XEVENTS */
   758 
   759             if (xevent.xproperty.atom == data->videodata->_NET_WM_STATE) {
   760                 /* Get the new state from the window manager.
   761                    Compositing window managers can alter visibility of windows
   762                    without ever mapping / unmapping them, so we handle that here,
   763                    because they use the NETWM protocol to notify us of changes.
   764                  */
   765                 Uint32 flags = X11_GetNetWMState(_this, xevent.xproperty.window);
   766                 if ((flags^data->window->flags) & SDL_WINDOW_HIDDEN) {
   767                     if (flags & SDL_WINDOW_HIDDEN) {
   768                         X11_DispatchUnmapNotify(data);
   769                     } else {
   770                         X11_DispatchMapNotify(data);
   771                     }
   772                 }
   773             }
   774         }
   775         break;
   776 
   777     /* Copy the selection from XA_CUT_BUFFER0 to the requested property */
   778     case SelectionRequest: {
   779             XSelectionRequestEvent *req;
   780             XEvent sevent;
   781             int seln_format;
   782             unsigned long nbytes;
   783             unsigned long overflow;
   784             unsigned char *seln_data;
   785 
   786             req = &xevent.xselectionrequest;
   787 #ifdef DEBUG_XEVENTS
   788             printf("window %p: SelectionRequest (requestor = %ld, target = %ld)\n", data,
   789                 req->requestor, req->target);
   790 #endif
   791 
   792             SDL_zero(sevent);
   793             sevent.xany.type = SelectionNotify;
   794             sevent.xselection.selection = req->selection;
   795             sevent.xselection.target = None;
   796             sevent.xselection.property = None;
   797             sevent.xselection.requestor = req->requestor;
   798             sevent.xselection.time = req->time;
   799             if (X11_XGetWindowProperty(display, DefaultRootWindow(display),
   800                     XA_CUT_BUFFER0, 0, INT_MAX/4, False, req->target,
   801                     &sevent.xselection.target, &seln_format, &nbytes,
   802                     &overflow, &seln_data) == Success) {
   803                 Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0);
   804                 if (sevent.xselection.target == req->target) {
   805                     X11_XChangeProperty(display, req->requestor, req->property,
   806                         sevent.xselection.target, seln_format, PropModeReplace,
   807                         seln_data, nbytes);
   808                     sevent.xselection.property = req->property;
   809                 } else if (XA_TARGETS == req->target) {
   810                     Atom SupportedFormats[] = { sevent.xselection.target, XA_TARGETS };
   811                     X11_XChangeProperty(display, req->requestor, req->property,
   812                         XA_ATOM, 32, PropModeReplace,
   813                         (unsigned char*)SupportedFormats,
   814                         sizeof(SupportedFormats)/sizeof(*SupportedFormats));
   815                     sevent.xselection.property = req->property;
   816                 }
   817                 X11_XFree(seln_data);
   818             }
   819             X11_XSendEvent(display, req->requestor, False, 0, &sevent);
   820             X11_XSync(display, False);
   821         }
   822         break;
   823 
   824     case SelectionNotify: {
   825 #ifdef DEBUG_XEVENTS
   826             printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data,
   827                 xevent.xselection.requestor, xevent.xselection.target);
   828 #endif
   829             Atom target = xevent.xselection.target;
   830             if (target == data->xdnd_req) {
   831                 /* read data */
   832                 SDL_x11Prop p;
   833                 X11_ReadProperty(&p, display, data->xwindow, videodata->PRIMARY);
   834 
   835                 if (p.format == 8) {
   836                     SDL_bool expect_lf = SDL_FALSE;
   837                     char *start = NULL;
   838                     char *scan = (char*)p.data;
   839                     char *fn;
   840                     char *uri;
   841                     int length = 0;
   842                     while (p.count--) {
   843                         if (!expect_lf) {
   844                             if (*scan == 0x0D) {
   845                                 expect_lf = SDL_TRUE;
   846                             }
   847                             if (start == NULL) {
   848                                 start = scan;
   849                                 length = 0;
   850                             }
   851                             length++;
   852                         } else {
   853                             if (*scan == 0x0A && length > 0) {
   854                                 uri = SDL_malloc(length--);
   855                                 SDL_memcpy(uri, start, length);
   856                                 uri[length] = '\0';
   857                                 fn = X11_URIToLocal(uri);
   858                                 if (fn) {
   859                                     SDL_SendDropFile(fn);
   860                                 }
   861                                 SDL_free(uri);
   862                             }
   863                             expect_lf = SDL_FALSE;
   864                             start = NULL;
   865                         }
   866                         scan++;
   867                     }
   868                 }
   869 
   870                 X11_XFree(p.data);
   871 
   872                 /* send reply */
   873                 SDL_memset(&m, 0, sizeof(XClientMessageEvent));
   874                 m.type = ClientMessage;
   875                 m.display = display;
   876                 m.window = data->xdnd_source;
   877                 m.message_type = videodata->XdndFinished;
   878                 m.format = 32;
   879                 m.data.l[0] = data->xwindow;
   880                 m.data.l[1] = 1;
   881                 m.data.l[2] = videodata->XdndActionCopy;
   882                 X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent*)&m);
   883 
   884                 X11_XSync(display, False);
   885 
   886             } else {
   887                 videodata->selection_waiting = SDL_FALSE;
   888             }
   889         }
   890         break;
   891 
   892     default:{
   893 #ifdef DEBUG_XEVENTS
   894             printf("window %p: Unhandled event %d\n", data, xevent.type);
   895 #endif
   896         }
   897         break;
   898     }
   899 }
   900 
   901 static void
   902 X11_HandleFocusChanges(_THIS)
   903 {
   904     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   905     int i;
   906 
   907     if (videodata && videodata->windowlist) {
   908         for (i = 0; i < videodata->numwindows; ++i) {
   909             SDL_WindowData *data = videodata->windowlist[i];
   910             if (data && data->pending_focus != PENDING_FOCUS_NONE) {
   911                 Uint32 now = SDL_GetTicks();
   912                 if (SDL_TICKS_PASSED(now, data->pending_focus_time)) {
   913                     if ( data->pending_focus == PENDING_FOCUS_IN ) {
   914                         X11_DispatchFocusIn(data);
   915                     } else {
   916                         X11_DispatchFocusOut(data);
   917                     }
   918                     data->pending_focus = PENDING_FOCUS_NONE;
   919                 }
   920             }
   921         }
   922     }
   923 }
   924 /* Ack!  X11_XPending() actually performs a blocking read if no events available */
   925 static int
   926 X11_Pending(Display * display)
   927 {
   928     /* Flush the display connection and look to see if events are queued */
   929     X11_XFlush(display);
   930     if (X11_XEventsQueued(display, QueuedAlready)) {
   931         return (1);
   932     }
   933 
   934     /* More drastic measures are required -- see if X is ready to talk */
   935     {
   936         static struct timeval zero_time;        /* static == 0 */
   937         int x11_fd;
   938         fd_set fdset;
   939 
   940         x11_fd = ConnectionNumber(display);
   941         FD_ZERO(&fdset);
   942         FD_SET(x11_fd, &fdset);
   943         if (select(x11_fd + 1, &fdset, NULL, NULL, &zero_time) == 1) {
   944             return (X11_XPending(display));
   945         }
   946     }
   947 
   948     /* Oh well, nothing is ready .. */
   949     return (0);
   950 }
   951 
   952 
   953 /* !!! FIXME: this should be exposed in a header, or something. */
   954 int SDL_GetNumTouch(void);
   955 void SDL_dbus_screensaver_tickle(_THIS);
   956 
   957 void
   958 X11_PumpEvents(_THIS)
   959 {
   960     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   961 
   962     /* Update activity every 30 seconds to prevent screensaver */
   963     if (_this->suspend_screensaver) {
   964         Uint32 now = SDL_GetTicks();
   965         if (!data->screensaver_activity ||
   966             SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) {
   967             X11_XResetScreenSaver(data->display);
   968 
   969             #if SDL_USE_LIBDBUS
   970             SDL_dbus_screensaver_tickle(_this);
   971             #endif
   972 
   973             data->screensaver_activity = now;
   974         }
   975     }
   976 
   977     /* Keep processing pending events */
   978     while (X11_Pending(data->display)) {
   979         X11_DispatchEvent(_this);
   980     }
   981 
   982     /* FIXME: Only need to do this when there are pending focus changes */
   983     X11_HandleFocusChanges(_this);
   984 }
   985 
   986 
   987 void
   988 X11_SuspendScreenSaver(_THIS)
   989 {
   990 #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER
   991     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   992     int dummy;
   993     int major_version, minor_version;
   994 
   995     if (SDL_X11_HAVE_XSS) {
   996         /* X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 */
   997         if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) ||
   998             !X11_XScreenSaverQueryVersion(data->display,
   999                                       &major_version, &minor_version) ||
  1000             major_version < 1 || (major_version == 1 && minor_version < 1)) {
  1001             return;
  1002         }
  1003 
  1004         X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver);
  1005         X11_XResetScreenSaver(data->display);
  1006     }
  1007 #endif
  1008 
  1009 #if SDL_USE_LIBDBUS
  1010     if (_this->suspend_screensaver) {
  1011         SDL_dbus_screensaver_tickle(_this);
  1012     }
  1013 #endif
  1014 }
  1015 
  1016 #endif /* SDL_VIDEO_DRIVER_X11 */
  1017 
  1018 /* vi: set ts=4 sw=4 expandtab: */