src/video/x11/SDL_x11events.c
author Sam Lantinga
Sun, 02 Feb 2014 00:53:27 -0800
changeset 8149 681eb46b8ac4
parent 8093 b43765095a6f
child 8229 437c4e5961e0
permissions -rw-r--r--
Fixed bug 2374 - Update copyright for 2014...

Is it that time already??
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_X11
    24 
    25 #include <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     /* If another window has already processed a focus in, then don't try to
   249      * remove focus here.  Doing so will incorrectly remove focus from that
   250      * window, and the focus lost event for this window will have already
   251      * been dispatched anyway. */
   252     if (data->window == SDL_GetKeyboardFocus()) {
   253         SDL_SetKeyboardFocus(NULL);
   254     }
   255 #ifdef X_HAVE_UTF8_STRING
   256     if (data->ic) {
   257         X11_XUnsetICFocus(data->ic);
   258     }
   259 #endif
   260 }
   261 
   262 static void
   263 X11_DispatchMapNotify(SDL_WindowData *data)
   264 {
   265     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   266     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
   267 }
   268 
   269 static void
   270 X11_DispatchUnmapNotify(SDL_WindowData *data)
   271 {
   272     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   273     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   274 }
   275 
   276 static void
   277 X11_DispatchEvent(_THIS)
   278 {
   279     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   280     Display *display = videodata->display;
   281     SDL_WindowData *data;
   282     XEvent xevent;
   283     int orig_event_type;
   284     KeyCode orig_keycode;
   285     XClientMessageEvent m;
   286     int i;
   287 
   288     SDL_zero(xevent);           /* valgrind fix. --ryan. */
   289     X11_XNextEvent(display, &xevent);
   290 
   291     /* Save the original keycode for dead keys, which are filtered out by
   292        the XFilterEvent() call below.
   293     */
   294     orig_event_type = xevent.type;
   295     if (orig_event_type == KeyPress || orig_event_type == KeyRelease) {
   296         orig_keycode = xevent.xkey.keycode;
   297     } else {
   298         orig_keycode = 0;
   299     }
   300 
   301     /* filter events catchs XIM events and sends them to the correct handler */
   302     if (X11_XFilterEvent(&xevent, None) == True) {
   303 #if 0
   304         printf("Filtered event type = %d display = %d window = %d\n",
   305                xevent.type, xevent.xany.display, xevent.xany.window);
   306 #endif
   307         if (orig_keycode) {
   308             /* Make sure dead key press/release events are sent */
   309             SDL_Scancode scancode = videodata->key_layout[orig_keycode];
   310             if (orig_event_type == KeyPress) {
   311                 SDL_SendKeyboardKey(SDL_PRESSED, scancode);
   312             } else {
   313                 SDL_SendKeyboardKey(SDL_RELEASED, scancode);
   314             }
   315         }
   316         return;
   317     }
   318 
   319     /* Send a SDL_SYSWMEVENT if the application wants them */
   320     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   321         SDL_SysWMmsg wmmsg;
   322 
   323         SDL_VERSION(&wmmsg.version);
   324         wmmsg.subsystem = SDL_SYSWM_X11;
   325         wmmsg.msg.x11.event = xevent;
   326         SDL_SendSysWMEvent(&wmmsg);
   327     }
   328 
   329 #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
   330     if(xevent.type == GenericEvent) {
   331         X11_HandleGenericEvent(videodata,xevent);
   332         return;
   333     }
   334 #endif
   335 
   336 #if 0
   337     printf("type = %d display = %d window = %d\n",
   338            xevent.type, xevent.xany.display, xevent.xany.window);
   339 #endif
   340 
   341     data = NULL;
   342     if (videodata && videodata->windowlist) {
   343         for (i = 0; i < videodata->numwindows; ++i) {
   344             if ((videodata->windowlist[i] != NULL) &&
   345                 (videodata->windowlist[i]->xwindow == xevent.xany.window)) {
   346                 data = videodata->windowlist[i];
   347                 break;
   348             }
   349         }
   350     }
   351     if (!data) {
   352         return;
   353     }
   354 
   355     switch (xevent.type) {
   356 
   357         /* Gaining mouse coverage? */
   358     case EnterNotify:{
   359 #ifdef DEBUG_XEVENTS
   360             printf("window %p: EnterNotify! (%d,%d,%d)\n", data,
   361                    xevent.xcrossing.x,
   362                    xevent.xcrossing.y,
   363                    xevent.xcrossing.mode);
   364             if (xevent.xcrossing.mode == NotifyGrab)
   365                 printf("Mode: NotifyGrab\n");
   366             if (xevent.xcrossing.mode == NotifyUngrab)
   367                 printf("Mode: NotifyUngrab\n");
   368 #endif
   369             SDL_SetMouseFocus(data->window);
   370 
   371             if (!SDL_GetMouse()->relative_mode) {
   372                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y);
   373             }
   374         }
   375         break;
   376         /* Losing mouse coverage? */
   377     case LeaveNotify:{
   378 #ifdef DEBUG_XEVENTS
   379             printf("window %p: LeaveNotify! (%d,%d,%d)\n", data,
   380                    xevent.xcrossing.x,
   381                    xevent.xcrossing.y,
   382                    xevent.xcrossing.mode);
   383             if (xevent.xcrossing.mode == NotifyGrab)
   384                 printf("Mode: NotifyGrab\n");
   385             if (xevent.xcrossing.mode == NotifyUngrab)
   386                 printf("Mode: NotifyUngrab\n");
   387 #endif
   388             if (!SDL_GetMouse()->relative_mode) {
   389                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y);
   390             }
   391 
   392             if (xevent.xcrossing.mode != NotifyGrab &&
   393                 xevent.xcrossing.mode != NotifyUngrab &&
   394                 xevent.xcrossing.detail != NotifyInferior) {
   395                 SDL_SetMouseFocus(NULL);
   396             }
   397         }
   398         break;
   399 
   400         /* Gaining input focus? */
   401     case FocusIn:{
   402             if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) {
   403                 /* Someone is handling a global hotkey, ignore it */
   404 #ifdef DEBUG_XEVENTS
   405                 printf("window %p: FocusIn (NotifyGrab/NotifyUngrab, ignoring)\n", data);
   406 #endif
   407                 break;
   408             }
   409 
   410             if (xevent.xfocus.detail == NotifyInferior) {
   411 #ifdef DEBUG_XEVENTS
   412                 printf("window %p: FocusIn (NotifierInferior, ignoring)\n", data);
   413 #endif
   414                 break;
   415             }
   416 #ifdef DEBUG_XEVENTS
   417             printf("window %p: FocusIn!\n", data);
   418 #endif
   419             if (data->pending_focus == PENDING_FOCUS_OUT &&
   420                 data->window == SDL_GetKeyboardFocus()) {
   421                 /* We want to reset the keyboard here, because we may have
   422                    missed keyboard messages after our previous FocusOut.
   423                  */
   424                 /* Actually, if we do this we clear the ALT key on Unity
   425                    because it briefly takes focus for their dashboard.
   426 
   427                    I think it's better to think the ALT key is held down
   428                    when it's not, then always lose the ALT modifier on Unity.
   429                  */
   430                 /* SDL_ResetKeyboard(); */
   431             }
   432             data->pending_focus = PENDING_FOCUS_IN;
   433             data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_IN_TIME;
   434         }
   435         break;
   436 
   437         /* Losing input focus? */
   438     case FocusOut:{
   439             if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) {
   440                 /* Someone is handling a global hotkey, ignore it */
   441 #ifdef DEBUG_XEVENTS
   442                 printf("window %p: FocusOut (NotifyGrab/NotifyUngrab, ignoring)\n", data);
   443 #endif
   444                 break;
   445             }
   446             if (xevent.xfocus.detail == NotifyInferior) {
   447                 /* We still have focus if a child gets focus */
   448 #ifdef DEBUG_XEVENTS
   449                 printf("window %p: FocusOut (NotifierInferior, ignoring)\n", data);
   450 #endif
   451                 break;
   452             }
   453 #ifdef DEBUG_XEVENTS
   454             printf("window %p: FocusOut!\n", data);
   455 #endif
   456             data->pending_focus = PENDING_FOCUS_OUT;
   457             data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_OUT_TIME;
   458         }
   459         break;
   460 
   461         /* Generated upon EnterWindow and FocusIn */
   462     case KeymapNotify:{
   463 #ifdef DEBUG_XEVENTS
   464             printf("window %p: KeymapNotify!\n", data);
   465 #endif
   466             /* FIXME:
   467                X11_SetKeyboardState(SDL_Display, xevent.xkeymap.key_vector);
   468              */
   469         }
   470         break;
   471 
   472         /* Has the keyboard layout changed? */
   473     case MappingNotify:{
   474 #ifdef DEBUG_XEVENTS
   475             printf("window %p: MappingNotify!\n", data);
   476 #endif
   477             X11_UpdateKeymap(_this);
   478         }
   479         break;
   480 
   481         /* Key press? */
   482     case KeyPress:{
   483             KeyCode keycode = xevent.xkey.keycode;
   484             KeySym keysym = NoSymbol;
   485             char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
   486             Status status = 0;
   487 
   488 #ifdef DEBUG_XEVENTS
   489             printf("window %p: KeyPress (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode);
   490 #endif
   491             SDL_SendKeyboardKey(SDL_PRESSED, videodata->key_layout[keycode]);
   492 #if 1
   493             if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) {
   494                 int min_keycode, max_keycode;
   495                 X11_XDisplayKeycodes(display, &min_keycode, &max_keycode);
   496 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
   497                 keysym = X11_XkbKeycodeToKeysym(display, keycode, 0, 0);
   498 #else
   499                 keysym = XKeycodeToKeysym(display, keycode, 0);
   500 #endif
   501                 fprintf(stderr,
   502                         "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",
   503                         keycode, keycode - min_keycode, keysym,
   504                         X11_XKeysymToString(keysym));
   505             }
   506 #endif
   507             /* */
   508             SDL_zero(text);
   509 #ifdef X_HAVE_UTF8_STRING
   510             if (data->ic) {
   511                 X11_Xutf8LookupString(data->ic, &xevent.xkey, text, sizeof(text),
   512                                   &keysym, &status);
   513             }
   514 #else
   515             XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL);
   516 #endif
   517             if (*text) {
   518                 SDL_SendKeyboardText(text);
   519             }
   520         }
   521         break;
   522 
   523         /* Key release? */
   524     case KeyRelease:{
   525             KeyCode keycode = xevent.xkey.keycode;
   526 
   527 #ifdef DEBUG_XEVENTS
   528             printf("window %p: KeyRelease (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode);
   529 #endif
   530             if (X11_KeyRepeat(display, &xevent)) {
   531                 /* We're about to get a repeated key down, ignore the key up */
   532                 break;
   533             }
   534             SDL_SendKeyboardKey(SDL_RELEASED, videodata->key_layout[keycode]);
   535         }
   536         break;
   537 
   538         /* Have we been iconified? */
   539     case UnmapNotify:{
   540 #ifdef DEBUG_XEVENTS
   541             printf("window %p: UnmapNotify!\n", data);
   542 #endif
   543             X11_DispatchUnmapNotify(data);
   544         }
   545         break;
   546 
   547         /* Have we been restored? */
   548     case MapNotify:{
   549 #ifdef DEBUG_XEVENTS
   550             printf("window %p: MapNotify!\n", data);
   551 #endif
   552             X11_DispatchMapNotify(data);
   553         }
   554         break;
   555 
   556         /* Have we been resized or moved? */
   557     case ConfigureNotify:{
   558 #ifdef DEBUG_XEVENTS
   559             printf("window %p: ConfigureNotify! (position: %d,%d, size: %dx%d)\n", data,
   560                    xevent.xconfigure.x, xevent.xconfigure.y,
   561                    xevent.xconfigure.width, xevent.xconfigure.height);
   562 #endif
   563             long border_left = 0;
   564             long border_right = 0;
   565             long border_top = 0;
   566             long border_bottom = 0;
   567             if (data->xwindow) {
   568                 Atom _net_frame_extents = X11_XInternAtom(display, "_NET_FRAME_EXTENTS", 0);
   569                 Atom type = None;
   570                 int format;
   571                 unsigned long nitems = 0, bytes_after;
   572                 unsigned char *property;
   573                 X11_XGetWindowProperty(display, data->xwindow,
   574                     _net_frame_extents, 0, 16, 0,
   575                     XA_CARDINAL, &type, &format,
   576                     &nitems, &bytes_after, &property);
   577 
   578                 if (type != None && nitems == 4)
   579                 {
   580                     border_left = ((long*)property)[0];
   581                     border_right = ((long*)property)[1];
   582                     border_top = ((long*)property)[2];
   583                     border_bottom = ((long*)property)[3];
   584                 }
   585             }
   586 
   587             if (xevent.xconfigure.x != data->last_xconfigure.x ||
   588                 xevent.xconfigure.y != data->last_xconfigure.y) {
   589                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED,
   590                                     xevent.xconfigure.x - border_left,
   591                                     xevent.xconfigure.y - border_top);
   592             }
   593             if (xevent.xconfigure.width != data->last_xconfigure.width ||
   594                 xevent.xconfigure.height != data->last_xconfigure.height) {
   595                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED,
   596                                     xevent.xconfigure.width,
   597                                     xevent.xconfigure.height);
   598             }
   599             data->last_xconfigure = xevent.xconfigure;
   600         }
   601         break;
   602 
   603         /* Have we been requested to quit (or another client message?) */
   604     case ClientMessage:{
   605 
   606             int xdnd_version=0;
   607 
   608             if (xevent.xclient.message_type == videodata->XdndEnter) {
   609                 SDL_bool use_list = xevent.xclient.data.l[1] & 1;
   610                 data->xdnd_source = xevent.xclient.data.l[0];
   611                 xdnd_version = ( xevent.xclient.data.l[1] >> 24);
   612                 if (use_list) {
   613                     /* fetch conversion targets */
   614                     SDL_x11Prop p;
   615                     X11_ReadProperty(&p, display, data->xdnd_source, videodata->XdndTypeList);
   616                     /* pick one */
   617                     data->xdnd_req = X11_PickTarget(display, (Atom*)p.data, p.count);
   618                     X11_XFree(p.data);
   619                 } else {
   620                     /* pick from list of three */
   621                     data->xdnd_req = X11_PickTargetFromAtoms(display, xevent.xclient.data.l[2], xevent.xclient.data.l[3], xevent.xclient.data.l[4]);
   622                 }
   623             }
   624             else if (xevent.xclient.message_type == videodata->XdndPosition) {
   625 
   626                 /* reply with status */
   627                 memset(&m, 0, sizeof(XClientMessageEvent));
   628                 m.type = ClientMessage;
   629                 m.display = xevent.xclient.display;
   630                 m.window = xevent.xclient.data.l[0];
   631                 m.message_type = videodata->XdndStatus;
   632                 m.format=32;
   633                 m.data.l[0] = data->xwindow;
   634                 m.data.l[1] = (data->xdnd_req != None);
   635                 m.data.l[2] = 0; /* specify an empty rectangle */
   636                 m.data.l[3] = 0;
   637                 m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */
   638 
   639                 X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
   640                 X11_XFlush(display);
   641             }
   642             else if(xevent.xclient.message_type == videodata->XdndDrop) {
   643                 if (data->xdnd_req == None) {
   644                     /* say again - not interested! */
   645                     memset(&m, 0, sizeof(XClientMessageEvent));
   646                     m.type = ClientMessage;
   647                     m.display = xevent.xclient.display;
   648                     m.window = xevent.xclient.data.l[0];
   649                     m.message_type = videodata->XdndFinished;
   650                     m.format=32;
   651                     m.data.l[0] = data->xwindow;
   652                     m.data.l[1] = 0;
   653                     m.data.l[2] = None; /* fail! */
   654                     X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
   655                 } else {
   656                     /* convert */
   657                     if(xdnd_version >= 1) {
   658                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent.xclient.data.l[2]);
   659                     } else {
   660                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime);
   661                     }
   662                 }
   663             }
   664             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
   665                 (xevent.xclient.format == 32) &&
   666                 (xevent.xclient.data.l[0] == videodata->_NET_WM_PING)) {
   667                 Window root = DefaultRootWindow(display);
   668 
   669 #ifdef DEBUG_XEVENTS
   670                 printf("window %p: _NET_WM_PING\n", data);
   671 #endif
   672                 xevent.xclient.window = root;
   673                 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xevent);
   674                 break;
   675             }
   676 
   677             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
   678                 (xevent.xclient.format == 32) &&
   679                 (xevent.xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) {
   680 
   681 #ifdef DEBUG_XEVENTS
   682                 printf("window %p: WM_DELETE_WINDOW\n", data);
   683 #endif
   684                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   685                 break;
   686             }
   687         }
   688         break;
   689 
   690         /* Do we need to refresh ourselves? */
   691     case Expose:{
   692 #ifdef DEBUG_XEVENTS
   693             printf("window %p: Expose (count = %d)\n", data, xevent.xexpose.count);
   694 #endif
   695             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
   696         }
   697         break;
   698 
   699     case MotionNotify:{
   700             SDL_Mouse *mouse = SDL_GetMouse();
   701             if(!mouse->relative_mode || mouse->relative_mode_warp) {
   702 #ifdef DEBUG_MOTION
   703                 printf("window %p: X11 motion: %d,%d\n", xevent.xmotion.x, xevent.xmotion.y);
   704 #endif
   705 
   706                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xmotion.x, xevent.xmotion.y);
   707             }
   708         }
   709         break;
   710 
   711     case ButtonPress:{
   712             int ticks = 0;
   713             if (X11_IsWheelEvent(display,&xevent,&ticks)) {
   714                 SDL_SendMouseWheel(data->window, 0, 0, ticks);
   715             } else {
   716                 SDL_SendMouseButton(data->window, 0, SDL_PRESSED, xevent.xbutton.button);
   717             }
   718         }
   719         break;
   720 
   721     case ButtonRelease:{
   722             SDL_SendMouseButton(data->window, 0, SDL_RELEASED, xevent.xbutton.button);
   723         }
   724         break;
   725 
   726     case PropertyNotify:{
   727 #ifdef DEBUG_XEVENTS
   728             unsigned char *propdata;
   729             int status, real_format;
   730             Atom real_type;
   731             unsigned long items_read, items_left, i;
   732 
   733             char *name = X11_XGetAtomName(display, xevent.xproperty.atom);
   734             if (name) {
   735                 printf("window %p: PropertyNotify: %s %s\n", data, name, (xevent.xproperty.state == PropertyDelete) ? "deleted" : "changed");
   736                 X11_XFree(name);
   737             }
   738 
   739             status = X11_XGetWindowProperty(display, data->xwindow, xevent.xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata);
   740             if (status == Success && items_read > 0) {
   741                 if (real_type == XA_INTEGER) {
   742                     int *values = (int *)propdata;
   743 
   744                     printf("{");
   745                     for (i = 0; i < items_read; i++) {
   746                         printf(" %d", values[i]);
   747                     }
   748                     printf(" }\n");
   749                 } else if (real_type == XA_CARDINAL) {
   750                     if (real_format == 32) {
   751                         Uint32 *values = (Uint32 *)propdata;
   752 
   753                         printf("{");
   754                         for (i = 0; i < items_read; i++) {
   755                             printf(" %d", values[i]);
   756                         }
   757                         printf(" }\n");
   758                     } else if (real_format == 16) {
   759                         Uint16 *values = (Uint16 *)propdata;
   760 
   761                         printf("{");
   762                         for (i = 0; i < items_read; i++) {
   763                             printf(" %d", values[i]);
   764                         }
   765                         printf(" }\n");
   766                     } else if (real_format == 8) {
   767                         Uint8 *values = (Uint8 *)propdata;
   768 
   769                         printf("{");
   770                         for (i = 0; i < items_read; i++) {
   771                             printf(" %d", values[i]);
   772                         }
   773                         printf(" }\n");
   774                     }
   775                 } else if (real_type == XA_STRING ||
   776                            real_type == videodata->UTF8_STRING) {
   777                     printf("{ \"%s\" }\n", propdata);
   778                 } else if (real_type == XA_ATOM) {
   779                     Atom *atoms = (Atom *)propdata;
   780 
   781                     printf("{");
   782                     for (i = 0; i < items_read; i++) {
   783                         char *name = X11_XGetAtomName(display, atoms[i]);
   784                         if (name) {
   785                             printf(" %s", name);
   786                             X11_XFree(name);
   787                         }
   788                     }
   789                     printf(" }\n");
   790                 } else {
   791                     char *name = X11_XGetAtomName(display, real_type);
   792                     printf("Unknown type: %ld (%s)\n", real_type, name ? name : "UNKNOWN");
   793                     if (name) {
   794                         X11_XFree(name);
   795                     }
   796                 }
   797             }
   798             if (status == Success) {
   799                 X11_XFree(propdata);
   800             }
   801 #endif /* DEBUG_XEVENTS */
   802 
   803             if (xevent.xproperty.atom == data->videodata->_NET_WM_STATE) {
   804                 /* Get the new state from the window manager.
   805                    Compositing window managers can alter visibility of windows
   806                    without ever mapping / unmapping them, so we handle that here,
   807                    because they use the NETWM protocol to notify us of changes.
   808                  */
   809                 Uint32 flags = X11_GetNetWMState(_this, xevent.xproperty.window);
   810                 if ((flags^data->window->flags) & SDL_WINDOW_HIDDEN) {
   811                     if (flags & SDL_WINDOW_HIDDEN) {
   812                         X11_DispatchUnmapNotify(data);
   813                     } else {
   814                         X11_DispatchMapNotify(data);
   815                     }
   816                 }
   817             }
   818         }
   819         break;
   820 
   821     /* Copy the selection from XA_CUT_BUFFER0 to the requested property */
   822     case SelectionRequest: {
   823             XSelectionRequestEvent *req;
   824             XEvent sevent;
   825             int seln_format;
   826             unsigned long nbytes;
   827             unsigned long overflow;
   828             unsigned char *seln_data;
   829 
   830             req = &xevent.xselectionrequest;
   831 #ifdef DEBUG_XEVENTS
   832             printf("window %p: SelectionRequest (requestor = %ld, target = %ld)\n", data,
   833                 req->requestor, req->target);
   834 #endif
   835 
   836             SDL_zero(sevent);
   837             sevent.xany.type = SelectionNotify;
   838             sevent.xselection.selection = req->selection;
   839             sevent.xselection.target = None;
   840             sevent.xselection.property = None;
   841             sevent.xselection.requestor = req->requestor;
   842             sevent.xselection.time = req->time;
   843             if (X11_XGetWindowProperty(display, DefaultRootWindow(display),
   844                     XA_CUT_BUFFER0, 0, INT_MAX/4, False, req->target,
   845                     &sevent.xselection.target, &seln_format, &nbytes,
   846                     &overflow, &seln_data) == Success) {
   847                 Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0);
   848                 if (sevent.xselection.target == req->target) {
   849                     X11_XChangeProperty(display, req->requestor, req->property,
   850                         sevent.xselection.target, seln_format, PropModeReplace,
   851                         seln_data, nbytes);
   852                     sevent.xselection.property = req->property;
   853                 } else if (XA_TARGETS == req->target) {
   854                     Atom SupportedFormats[] = { sevent.xselection.target, XA_TARGETS };
   855                     X11_XChangeProperty(display, req->requestor, req->property,
   856                         XA_ATOM, 32, PropModeReplace,
   857                         (unsigned char*)SupportedFormats,
   858                         sizeof(SupportedFormats)/sizeof(*SupportedFormats));
   859                     sevent.xselection.property = req->property;
   860                 }
   861                 X11_XFree(seln_data);
   862             }
   863             X11_XSendEvent(display, req->requestor, False, 0, &sevent);
   864             X11_XSync(display, False);
   865         }
   866         break;
   867 
   868     case SelectionNotify: {
   869 #ifdef DEBUG_XEVENTS
   870             printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data,
   871                 xevent.xselection.requestor, xevent.xselection.target);
   872 #endif
   873             Atom target = xevent.xselection.target;
   874             if (target == data->xdnd_req) {
   875                 /* read data */
   876                 SDL_x11Prop p;
   877                 X11_ReadProperty(&p, display, data->xwindow, videodata->PRIMARY);
   878 
   879                 if (p.format == 8) {
   880                     SDL_bool expect_lf = SDL_FALSE;
   881                     char *start = NULL;
   882                     char *scan = (char*)p.data;
   883                     char *fn;
   884                     char *uri;
   885                     int length = 0;
   886                     while (p.count--) {
   887                         if (!expect_lf) {
   888                             if (*scan == 0x0D) {
   889                                 expect_lf = SDL_TRUE;
   890                             }
   891                             if (start == NULL) {
   892                                 start = scan;
   893                                 length = 0;
   894                             }
   895                             length++;
   896                         } else {
   897                             if (*scan == 0x0A && length > 0) {
   898                                 uri = SDL_malloc(length--);
   899                                 SDL_memcpy(uri, start, length);
   900                                 uri[length] = '\0';
   901                                 fn = X11_URIToLocal(uri);
   902                                 if (fn) {
   903                                     SDL_SendDropFile(fn);
   904                                 }
   905                                 SDL_free(uri);
   906                             }
   907                             expect_lf = SDL_FALSE;
   908                             start = NULL;
   909                         }
   910                         scan++;
   911                     }
   912                 }
   913 
   914                 X11_XFree(p.data);
   915 
   916                 /* send reply */
   917                 SDL_memset(&m, 0, sizeof(XClientMessageEvent));
   918                 m.type = ClientMessage;
   919                 m.display = display;
   920                 m.window = data->xdnd_source;
   921                 m.message_type = videodata->XdndFinished;
   922                 m.format = 32;
   923                 m.data.l[0] = data->xwindow;
   924                 m.data.l[1] = 1;
   925                 m.data.l[2] = videodata->XdndActionCopy;
   926                 X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent*)&m);
   927 
   928                 X11_XSync(display, False);
   929 
   930             } else {
   931                 videodata->selection_waiting = SDL_FALSE;
   932             }
   933         }
   934         break;
   935 
   936     default:{
   937 #ifdef DEBUG_XEVENTS
   938             printf("window %p: Unhandled event %d\n", data, xevent.type);
   939 #endif
   940         }
   941         break;
   942     }
   943 }
   944 
   945 static void
   946 X11_HandleFocusChanges(_THIS)
   947 {
   948     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   949     int i;
   950 
   951     if (videodata && videodata->windowlist) {
   952         for (i = 0; i < videodata->numwindows; ++i) {
   953             SDL_WindowData *data = videodata->windowlist[i];
   954             if (data && data->pending_focus != PENDING_FOCUS_NONE) {
   955                 Uint32 now = SDL_GetTicks();
   956                 if (SDL_TICKS_PASSED(now, data->pending_focus_time)) {
   957                     if ( data->pending_focus == PENDING_FOCUS_IN ) {
   958                         X11_DispatchFocusIn(data);
   959                     } else {
   960                         X11_DispatchFocusOut(data);
   961                     }
   962                     data->pending_focus = PENDING_FOCUS_NONE;
   963                 }
   964             }
   965         }
   966     }
   967 }
   968 /* Ack!  X11_XPending() actually performs a blocking read if no events available */
   969 static int
   970 X11_Pending(Display * display)
   971 {
   972     /* Flush the display connection and look to see if events are queued */
   973     X11_XFlush(display);
   974     if (X11_XEventsQueued(display, QueuedAlready)) {
   975         return (1);
   976     }
   977 
   978     /* More drastic measures are required -- see if X is ready to talk */
   979     {
   980         static struct timeval zero_time;        /* static == 0 */
   981         int x11_fd;
   982         fd_set fdset;
   983 
   984         x11_fd = ConnectionNumber(display);
   985         FD_ZERO(&fdset);
   986         FD_SET(x11_fd, &fdset);
   987         if (select(x11_fd + 1, &fdset, NULL, NULL, &zero_time) == 1) {
   988             return (X11_XPending(display));
   989         }
   990     }
   991 
   992     /* Oh well, nothing is ready .. */
   993     return (0);
   994 }
   995 
   996 
   997 /* !!! FIXME: this should be exposed in a header, or something. */
   998 int SDL_GetNumTouch(void);
   999 void SDL_dbus_screensaver_tickle(_THIS);
  1000 
  1001 void
  1002 X11_PumpEvents(_THIS)
  1003 {
  1004     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
  1005 
  1006     /* Update activity every 30 seconds to prevent screensaver */
  1007     if (_this->suspend_screensaver) {
  1008         Uint32 now = SDL_GetTicks();
  1009         if (!data->screensaver_activity ||
  1010             SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) {
  1011             X11_XResetScreenSaver(data->display);
  1012 
  1013             #if SDL_USE_LIBDBUS
  1014             SDL_dbus_screensaver_tickle(_this);
  1015             #endif
  1016 
  1017             data->screensaver_activity = now;
  1018         }
  1019     }
  1020 
  1021     /* Keep processing pending events */
  1022     while (X11_Pending(data->display)) {
  1023         X11_DispatchEvent(_this);
  1024     }
  1025 
  1026     /* FIXME: Only need to do this when there are pending focus changes */
  1027     X11_HandleFocusChanges(_this);
  1028 }
  1029 
  1030 
  1031 void
  1032 X11_SuspendScreenSaver(_THIS)
  1033 {
  1034 #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER
  1035     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
  1036     int dummy;
  1037     int major_version, minor_version;
  1038 
  1039     if (SDL_X11_HAVE_XSS) {
  1040         /* X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 */
  1041         if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) ||
  1042             !X11_XScreenSaverQueryVersion(data->display,
  1043                                       &major_version, &minor_version) ||
  1044             major_version < 1 || (major_version == 1 && minor_version < 1)) {
  1045             return;
  1046         }
  1047 
  1048         X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver);
  1049         X11_XResetScreenSaver(data->display);
  1050     }
  1051 #endif
  1052 
  1053 #if SDL_USE_LIBDBUS
  1054     if (_this->suspend_screensaver) {
  1055         SDL_dbus_screensaver_tickle(_this);
  1056     }
  1057 #endif
  1058 }
  1059 
  1060 #endif /* SDL_VIDEO_DRIVER_X11 */
  1061 
  1062 /* vi: set ts=4 sw=4 expandtab: */