src/video/x11/SDL_x11events.c
author Pierre-Loup A. Griffais
Wed, 23 Oct 2013 13:43:17 -0700
changeset 7885 a8bd63b33636
parent 7857 6388f5229bb7
child 7902 0c2e2b91eeea
permissions -rw-r--r--
Fix regression causing crashes when running without a window manager.
     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 = None;
   529                 int format;
   530                 unsigned long nitems = 0, 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                 if (type != None && nitems == 4)
   538                 {
   539                     border_left = ((long*)property)[0];
   540                     border_right = ((long*)property)[1];
   541                     border_top = ((long*)property)[2];
   542                     border_bottom = ((long*)property)[3];
   543                 }
   544             }
   545 
   546             if (xevent.xconfigure.x != data->last_xconfigure.x ||
   547                 xevent.xconfigure.y != data->last_xconfigure.y) {
   548                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED,
   549                                     xevent.xconfigure.x - border_left,
   550                                     xevent.xconfigure.y - border_top);
   551             }
   552             if (xevent.xconfigure.width != data->last_xconfigure.width ||
   553                 xevent.xconfigure.height != data->last_xconfigure.height) {
   554                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED,
   555                                     xevent.xconfigure.width,
   556                                     xevent.xconfigure.height);
   557             }
   558             data->last_xconfigure = xevent.xconfigure;
   559         }
   560         break;
   561 
   562         /* Have we been requested to quit (or another client message?) */
   563     case ClientMessage:{
   564 
   565             int xdnd_version=0;
   566 
   567             if (xevent.xclient.message_type == videodata->XdndEnter) {
   568                 SDL_bool use_list = xevent.xclient.data.l[1] & 1;
   569                 data->xdnd_source = xevent.xclient.data.l[0];
   570                 xdnd_version = ( xevent.xclient.data.l[1] >> 24);
   571                 if (use_list) {
   572                     /* fetch conversion targets */
   573                     SDL_x11Prop p;
   574                     X11_ReadProperty(&p, display, data->xdnd_source, videodata->XdndTypeList);
   575                     /* pick one */
   576                     data->xdnd_req = X11_PickTarget(display, (Atom*)p.data, p.count);
   577                     X11_XFree(p.data);
   578                 } else {
   579                     /* pick from list of three */
   580                     data->xdnd_req = X11_PickTargetFromAtoms(display, xevent.xclient.data.l[2], xevent.xclient.data.l[3], xevent.xclient.data.l[4]);
   581                 }
   582             }
   583             else if (xevent.xclient.message_type == videodata->XdndPosition) {
   584 
   585                 /* reply with status */
   586                 memset(&m, 0, sizeof(XClientMessageEvent));
   587                 m.type = ClientMessage;
   588                 m.display = xevent.xclient.display;
   589                 m.window = xevent.xclient.data.l[0];
   590                 m.message_type = videodata->XdndStatus;
   591                 m.format=32;
   592                 m.data.l[0] = data->xwindow;
   593                 m.data.l[1] = (data->xdnd_req != None);
   594                 m.data.l[2] = 0; /* specify an empty rectangle */
   595                 m.data.l[3] = 0;
   596                 m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */
   597 
   598                 X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
   599                 X11_XFlush(display);
   600             }
   601             else if(xevent.xclient.message_type == videodata->XdndDrop) {
   602                 if (data->xdnd_req == None) {
   603                     /* say again - not interested! */
   604                     memset(&m, 0, sizeof(XClientMessageEvent));
   605                     m.type = ClientMessage;
   606                     m.display = xevent.xclient.display;
   607                     m.window = xevent.xclient.data.l[0];
   608                     m.message_type = videodata->XdndFinished;
   609                     m.format=32;
   610                     m.data.l[0] = data->xwindow;
   611                     m.data.l[1] = 0;
   612                     m.data.l[2] = None; /* fail! */
   613                     X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
   614                 } else {
   615                     /* convert */
   616                     if(xdnd_version >= 1) {
   617                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent.xclient.data.l[2]);
   618                     } else {
   619                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime);
   620                     }
   621                 }
   622             }
   623             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
   624                 (xevent.xclient.format == 32) &&
   625                 (xevent.xclient.data.l[0] == videodata->_NET_WM_PING)) {
   626                 Window root = DefaultRootWindow(display);
   627 
   628 #ifdef DEBUG_XEVENTS
   629                 printf("window %p: _NET_WM_PING\n", data);
   630 #endif
   631                 xevent.xclient.window = root;
   632                 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xevent);
   633                 break;
   634             }
   635 
   636             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
   637                 (xevent.xclient.format == 32) &&
   638                 (xevent.xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) {
   639 
   640 #ifdef DEBUG_XEVENTS
   641                 printf("window %p: WM_DELETE_WINDOW\n", data);
   642 #endif
   643                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   644                 break;
   645             }
   646         }
   647         break;
   648 
   649         /* Do we need to refresh ourselves? */
   650     case Expose:{
   651 #ifdef DEBUG_XEVENTS
   652             printf("window %p: Expose (count = %d)\n", data, xevent.xexpose.count);
   653 #endif
   654             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
   655         }
   656         break;
   657 
   658     case MotionNotify:{
   659             SDL_Mouse *mouse = SDL_GetMouse();
   660             if(!mouse->relative_mode) {
   661 #ifdef DEBUG_MOTION
   662                 printf("window %p: X11 motion: %d,%d\n", xevent.xmotion.x, xevent.xmotion.y);
   663 #endif
   664 
   665                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xmotion.x, xevent.xmotion.y);
   666             }
   667         }
   668         break;
   669 
   670     case ButtonPress:{
   671             int ticks = 0;
   672             if (X11_IsWheelEvent(display,&xevent,&ticks)) {
   673                 SDL_SendMouseWheel(data->window, 0, 0, ticks);
   674             } else {
   675                 SDL_SendMouseButton(data->window, 0, SDL_PRESSED, xevent.xbutton.button);
   676             }
   677         }
   678         break;
   679 
   680     case ButtonRelease:{
   681             SDL_SendMouseButton(data->window, 0, SDL_RELEASED, xevent.xbutton.button);
   682         }
   683         break;
   684 
   685     case PropertyNotify:{
   686 #ifdef DEBUG_XEVENTS
   687             unsigned char *propdata;
   688             int status, real_format;
   689             Atom real_type;
   690             unsigned long items_read, items_left, i;
   691 
   692             char *name = X11_XGetAtomName(display, xevent.xproperty.atom);
   693             if (name) {
   694                 printf("window %p: PropertyNotify: %s %s\n", data, name, (xevent.xproperty.state == PropertyDelete) ? "deleted" : "changed");
   695                 X11_XFree(name);
   696             }
   697 
   698             status = X11_XGetWindowProperty(display, data->xwindow, xevent.xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata);
   699             if (status == Success && items_read > 0) {
   700                 if (real_type == XA_INTEGER) {
   701                     int *values = (int *)propdata;
   702 
   703                     printf("{");
   704                     for (i = 0; i < items_read; i++) {
   705                         printf(" %d", values[i]);
   706                     }
   707                     printf(" }\n");
   708                 } else if (real_type == XA_CARDINAL) {
   709                     if (real_format == 32) {
   710                         Uint32 *values = (Uint32 *)propdata;
   711 
   712                         printf("{");
   713                         for (i = 0; i < items_read; i++) {
   714                             printf(" %d", values[i]);
   715                         }
   716                         printf(" }\n");
   717                     } else if (real_format == 16) {
   718                         Uint16 *values = (Uint16 *)propdata;
   719 
   720                         printf("{");
   721                         for (i = 0; i < items_read; i++) {
   722                             printf(" %d", values[i]);
   723                         }
   724                         printf(" }\n");
   725                     } else if (real_format == 8) {
   726                         Uint8 *values = (Uint8 *)propdata;
   727 
   728                         printf("{");
   729                         for (i = 0; i < items_read; i++) {
   730                             printf(" %d", values[i]);
   731                         }
   732                         printf(" }\n");
   733                     }
   734                 } else if (real_type == XA_STRING ||
   735                            real_type == videodata->UTF8_STRING) {
   736                     printf("{ \"%s\" }\n", propdata);
   737                 } else if (real_type == XA_ATOM) {
   738                     Atom *atoms = (Atom *)propdata;
   739 
   740                     printf("{");
   741                     for (i = 0; i < items_read; i++) {
   742                         char *name = X11_XGetAtomName(display, atoms[i]);
   743                         if (name) {
   744                             printf(" %s", name);
   745                             X11_XFree(name);
   746                         }
   747                     }
   748                     printf(" }\n");
   749                 } else {
   750                     char *name = X11_XGetAtomName(display, real_type);
   751                     printf("Unknown type: %ld (%s)\n", real_type, name ? name : "UNKNOWN");
   752                     if (name) {
   753                         X11_XFree(name);
   754                     }
   755                 }
   756             }
   757             if (status == Success) {
   758                 X11_XFree(propdata);
   759             }
   760 #endif /* DEBUG_XEVENTS */
   761 
   762             if (xevent.xproperty.atom == data->videodata->_NET_WM_STATE) {
   763                 /* Get the new state from the window manager.
   764                    Compositing window managers can alter visibility of windows
   765                    without ever mapping / unmapping them, so we handle that here,
   766                    because they use the NETWM protocol to notify us of changes.
   767                  */
   768                 Uint32 flags = X11_GetNetWMState(_this, xevent.xproperty.window);
   769                 if ((flags^data->window->flags) & SDL_WINDOW_HIDDEN) {
   770                     if (flags & SDL_WINDOW_HIDDEN) {
   771                         X11_DispatchUnmapNotify(data);
   772                     } else {
   773                         X11_DispatchMapNotify(data);
   774                     }
   775                 }
   776             }
   777         }
   778         break;
   779 
   780     /* Copy the selection from XA_CUT_BUFFER0 to the requested property */
   781     case SelectionRequest: {
   782             XSelectionRequestEvent *req;
   783             XEvent sevent;
   784             int seln_format;
   785             unsigned long nbytes;
   786             unsigned long overflow;
   787             unsigned char *seln_data;
   788 
   789             req = &xevent.xselectionrequest;
   790 #ifdef DEBUG_XEVENTS
   791             printf("window %p: SelectionRequest (requestor = %ld, target = %ld)\n", data,
   792                 req->requestor, req->target);
   793 #endif
   794 
   795             SDL_zero(sevent);
   796             sevent.xany.type = SelectionNotify;
   797             sevent.xselection.selection = req->selection;
   798             sevent.xselection.target = None;
   799             sevent.xselection.property = None;
   800             sevent.xselection.requestor = req->requestor;
   801             sevent.xselection.time = req->time;
   802             if (X11_XGetWindowProperty(display, DefaultRootWindow(display),
   803                     XA_CUT_BUFFER0, 0, INT_MAX/4, False, req->target,
   804                     &sevent.xselection.target, &seln_format, &nbytes,
   805                     &overflow, &seln_data) == Success) {
   806                 Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0);
   807                 if (sevent.xselection.target == req->target) {
   808                     X11_XChangeProperty(display, req->requestor, req->property,
   809                         sevent.xselection.target, seln_format, PropModeReplace,
   810                         seln_data, nbytes);
   811                     sevent.xselection.property = req->property;
   812                 } else if (XA_TARGETS == req->target) {
   813                     Atom SupportedFormats[] = { sevent.xselection.target, XA_TARGETS };
   814                     X11_XChangeProperty(display, req->requestor, req->property,
   815                         XA_ATOM, 32, PropModeReplace,
   816                         (unsigned char*)SupportedFormats,
   817                         sizeof(SupportedFormats)/sizeof(*SupportedFormats));
   818                     sevent.xselection.property = req->property;
   819                 }
   820                 X11_XFree(seln_data);
   821             }
   822             X11_XSendEvent(display, req->requestor, False, 0, &sevent);
   823             X11_XSync(display, False);
   824         }
   825         break;
   826 
   827     case SelectionNotify: {
   828 #ifdef DEBUG_XEVENTS
   829             printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data,
   830                 xevent.xselection.requestor, xevent.xselection.target);
   831 #endif
   832             Atom target = xevent.xselection.target;
   833             if (target == data->xdnd_req) {
   834                 /* read data */
   835                 SDL_x11Prop p;
   836                 X11_ReadProperty(&p, display, data->xwindow, videodata->PRIMARY);
   837 
   838                 if (p.format == 8) {
   839                     SDL_bool expect_lf = SDL_FALSE;
   840                     char *start = NULL;
   841                     char *scan = (char*)p.data;
   842                     char *fn;
   843                     char *uri;
   844                     int length = 0;
   845                     while (p.count--) {
   846                         if (!expect_lf) {
   847                             if (*scan == 0x0D) {
   848                                 expect_lf = SDL_TRUE;
   849                             }
   850                             if (start == NULL) {
   851                                 start = scan;
   852                                 length = 0;
   853                             }
   854                             length++;
   855                         } else {
   856                             if (*scan == 0x0A && length > 0) {
   857                                 uri = SDL_malloc(length--);
   858                                 SDL_memcpy(uri, start, length);
   859                                 uri[length] = '\0';
   860                                 fn = X11_URIToLocal(uri);
   861                                 if (fn) {
   862                                     SDL_SendDropFile(fn);
   863                                 }
   864                                 SDL_free(uri);
   865                             }
   866                             expect_lf = SDL_FALSE;
   867                             start = NULL;
   868                         }
   869                         scan++;
   870                     }
   871                 }
   872 
   873                 X11_XFree(p.data);
   874 
   875                 /* send reply */
   876                 SDL_memset(&m, 0, sizeof(XClientMessageEvent));
   877                 m.type = ClientMessage;
   878                 m.display = display;
   879                 m.window = data->xdnd_source;
   880                 m.message_type = videodata->XdndFinished;
   881                 m.format = 32;
   882                 m.data.l[0] = data->xwindow;
   883                 m.data.l[1] = 1;
   884                 m.data.l[2] = videodata->XdndActionCopy;
   885                 X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent*)&m);
   886 
   887                 X11_XSync(display, False);
   888 
   889             } else {
   890                 videodata->selection_waiting = SDL_FALSE;
   891             }
   892         }
   893         break;
   894 
   895     default:{
   896 #ifdef DEBUG_XEVENTS
   897             printf("window %p: Unhandled event %d\n", data, xevent.type);
   898 #endif
   899         }
   900         break;
   901     }
   902 }
   903 
   904 static void
   905 X11_HandleFocusChanges(_THIS)
   906 {
   907     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   908     int i;
   909 
   910     if (videodata && videodata->windowlist) {
   911         for (i = 0; i < videodata->numwindows; ++i) {
   912             SDL_WindowData *data = videodata->windowlist[i];
   913             if (data && data->pending_focus != PENDING_FOCUS_NONE) {
   914                 Uint32 now = SDL_GetTicks();
   915                 if (SDL_TICKS_PASSED(now, data->pending_focus_time)) {
   916                     if ( data->pending_focus == PENDING_FOCUS_IN ) {
   917                         X11_DispatchFocusIn(data);
   918                     } else {
   919                         X11_DispatchFocusOut(data);
   920                     }
   921                     data->pending_focus = PENDING_FOCUS_NONE;
   922                 }
   923             }
   924         }
   925     }
   926 }
   927 /* Ack!  X11_XPending() actually performs a blocking read if no events available */
   928 static int
   929 X11_Pending(Display * display)
   930 {
   931     /* Flush the display connection and look to see if events are queued */
   932     X11_XFlush(display);
   933     if (X11_XEventsQueued(display, QueuedAlready)) {
   934         return (1);
   935     }
   936 
   937     /* More drastic measures are required -- see if X is ready to talk */
   938     {
   939         static struct timeval zero_time;        /* static == 0 */
   940         int x11_fd;
   941         fd_set fdset;
   942 
   943         x11_fd = ConnectionNumber(display);
   944         FD_ZERO(&fdset);
   945         FD_SET(x11_fd, &fdset);
   946         if (select(x11_fd + 1, &fdset, NULL, NULL, &zero_time) == 1) {
   947             return (X11_XPending(display));
   948         }
   949     }
   950 
   951     /* Oh well, nothing is ready .. */
   952     return (0);
   953 }
   954 
   955 
   956 /* !!! FIXME: this should be exposed in a header, or something. */
   957 int SDL_GetNumTouch(void);
   958 void SDL_dbus_screensaver_tickle(_THIS);
   959 
   960 void
   961 X11_PumpEvents(_THIS)
   962 {
   963     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   964 
   965     /* Update activity every 30 seconds to prevent screensaver */
   966     if (_this->suspend_screensaver) {
   967         Uint32 now = SDL_GetTicks();
   968         if (!data->screensaver_activity ||
   969             SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) {
   970             X11_XResetScreenSaver(data->display);
   971 
   972             #if SDL_USE_LIBDBUS
   973             SDL_dbus_screensaver_tickle(_this);
   974             #endif
   975 
   976             data->screensaver_activity = now;
   977         }
   978     }
   979 
   980     /* Keep processing pending events */
   981     while (X11_Pending(data->display)) {
   982         X11_DispatchEvent(_this);
   983     }
   984 
   985     /* FIXME: Only need to do this when there are pending focus changes */
   986     X11_HandleFocusChanges(_this);
   987 }
   988 
   989 
   990 void
   991 X11_SuspendScreenSaver(_THIS)
   992 {
   993 #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER
   994     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   995     int dummy;
   996     int major_version, minor_version;
   997 
   998     if (SDL_X11_HAVE_XSS) {
   999         /* X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 */
  1000         if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) ||
  1001             !X11_XScreenSaverQueryVersion(data->display,
  1002                                       &major_version, &minor_version) ||
  1003             major_version < 1 || (major_version == 1 && minor_version < 1)) {
  1004             return;
  1005         }
  1006 
  1007         X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver);
  1008         X11_XResetScreenSaver(data->display);
  1009     }
  1010 #endif
  1011 
  1012 #if SDL_USE_LIBDBUS
  1013     if (_this->suspend_screensaver) {
  1014         SDL_dbus_screensaver_tickle(_this);
  1015     }
  1016 #endif
  1017 }
  1018 
  1019 #endif /* SDL_VIDEO_DRIVER_X11 */
  1020 
  1021 /* vi: set ts=4 sw=4 expandtab: */