Some drag'and'drop improvements.
authorRyan C. Gordon <icculus@icculus.org>
Thu, 02 Aug 2018 16:03:47 -0400
changeset 120701d65571b57dd
parent 12069 317db3d3712c
child 12071 1f8341f599c4
Some drag'and'drop improvements.

First: disable d'n'd events by default; most apps don't need these at all, and
if an app doesn't explicitly handle these, each drop on the window will cause
a memory leak if the events are enabled. This follows the guidelines we have
for SDL_TEXTINPUT events already.

Second: when events are enabled or disabled, signal the video layer, as it
might be able to inform the OS, causing UI changes or optimizations (for
example, dropping a file icon on a Cocoa app that isn't accepting drops will
cause macOS to show a rejection animation instead of the drop operation just
vanishing into the ether, X11 might show a different cursor when dragging
onto an accepting window, etc).

Third: fill in the drop event details in the test library and enable the
events in testwm.c for making sure this all works as expected.
src/events/SDL_events.c
src/test/SDL_test_common.c
src/video/SDL_sysvideo.h
src/video/SDL_video.c
src/video/cocoa/SDL_cocoavideo.m
src/video/cocoa/SDL_cocoawindow.h
src/video/cocoa/SDL_cocoawindow.m
src/video/windows/SDL_windowsvideo.c
src/video/windows/SDL_windowswindow.c
src/video/windows/SDL_windowswindow.h
src/video/x11/SDL_x11video.c
src/video/x11/SDL_x11window.c
src/video/x11/SDL_x11window.h
test/testwm2.c
     1.1 --- a/src/events/SDL_events.c	Sun Jul 22 19:42:08 2018 -0400
     1.2 +++ b/src/events/SDL_events.c	Thu Aug 02 16:03:47 2018 -0400
     1.3 @@ -417,6 +417,8 @@
     1.4      SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);
     1.5      SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
     1.6      SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
     1.7 +    SDL_EventState(SDL_DROPFILE, SDL_DISABLE);
     1.8 +    SDL_EventState(SDL_DROPTEXT, SDL_DISABLE);
     1.9  
    1.10      SDL_AtomicSet(&SDL_EventQ.active, 1);
    1.11  
    1.12 @@ -604,6 +606,10 @@
    1.13  void
    1.14  SDL_FlushEvents(Uint32 minType, Uint32 maxType)
    1.15  {
    1.16 +    /* !!! FIXME: we need to manually SDL_free() the strings in TEXTINPUT and
    1.17 +       drag'n'drop events if we're flushing them without passing them to the
    1.18 +       app, but I don't know if this is the right place to do that. */
    1.19 +
    1.20      /* Don't look after we've quit */
    1.21      if (!SDL_AtomicGet(&SDL_EventQ.active)) {
    1.22          return;
    1.23 @@ -863,6 +869,8 @@
    1.24  Uint8
    1.25  SDL_EventState(Uint32 type, int state)
    1.26  {
    1.27 +    const SDL_bool isdnd = ((state == SDL_DISABLE) || (state == SDL_ENABLE)) &&
    1.28 +                           ((type == SDL_DROPFILE) || (type == SDL_DROPTEXT));
    1.29      Uint8 current_state;
    1.30      Uint8 hi = ((type >> 8) & 0xff);
    1.31      Uint8 lo = (type & 0xff);
    1.32 @@ -898,6 +906,12 @@
    1.33          }
    1.34      }
    1.35  
    1.36 +    /* turn off drag'n'drop support if we've disabled the events.
    1.37 +       This might change some UI details at the OS level. */
    1.38 +    if (isdnd) {
    1.39 +        SDL_ToggleDragAndDropSupport();
    1.40 +    }
    1.41 +
    1.42      return current_state;
    1.43  }
    1.44  
     2.1 --- a/src/test/SDL_test_common.c	Sun Jul 22 19:42:08 2018 -0400
     2.2 +++ b/src/test/SDL_test_common.c	Thu Aug 02 16:03:47 2018 -0400
     2.3 @@ -1349,7 +1349,18 @@
     2.4      case SDL_APP_DIDENTERFOREGROUND:
     2.5          SDL_Log("SDL EVENT: App entered the foreground");
     2.6          break;
     2.7 -
     2.8 +    case SDL_DROPBEGIN:
     2.9 +        SDL_Log("SDL EVENT: Drag and drop beginning");
    2.10 +        break;
    2.11 +    case SDL_DROPFILE:
    2.12 +        SDL_Log("SDL EVENT: Drag and drop file: '%s'", event->drop.file);
    2.13 +        break;
    2.14 +    case SDL_DROPTEXT:
    2.15 +        SDL_Log("SDL EVENT: Drag and drop text: '%s'", event->drop.file);
    2.16 +        break;
    2.17 +    case SDL_DROPCOMPLETE:
    2.18 +        SDL_Log("SDL EVENT: Drag and drop ending");
    2.19 +        break;
    2.20      case SDL_QUIT:
    2.21          SDL_Log("SDL EVENT: Quit requested");
    2.22          break;
    2.23 @@ -1744,6 +1755,11 @@
    2.24      case SDL_MOUSEMOTION:
    2.25          lastEvent = event->motion;
    2.26          break;
    2.27 +
    2.28 +    case SDL_DROPFILE:
    2.29 +    case SDL_DROPTEXT:
    2.30 +        SDL_free(event->drop.file);
    2.31 +        break;
    2.32      }
    2.33  }
    2.34  
     3.1 --- a/src/video/SDL_sysvideo.h	Sun Jul 22 19:42:08 2018 -0400
     3.2 +++ b/src/video/SDL_sysvideo.h	Thu Aug 02 16:03:47 2018 -0400
     3.3 @@ -304,6 +304,9 @@
     3.4      /* Hit-testing */
     3.5      int (*SetWindowHitTest)(SDL_Window * window, SDL_bool enabled);
     3.6  
     3.7 +    /* Tell window that app enabled drag'n'drop events */
     3.8 +    void (*AcceptDragAndDrop)(SDL_Window * window, SDL_bool accept);
     3.9 +
    3.10      /* * * */
    3.11      /* Data common to all drivers */
    3.12      SDL_bool is_dummy;
    3.13 @@ -454,6 +457,8 @@
    3.14  extern void SDL_OnApplicationWillEnterForeground(void);
    3.15  extern void SDL_OnApplicationDidBecomeActive(void);
    3.16  
    3.17 +extern void SDL_ToggleDragAndDropSupport(void);
    3.18 +
    3.19  #endif /* SDL_sysvideo_h_ */
    3.20  
    3.21  /* vi: set ts=4 sw=4 expandtab: */
     4.1 --- a/src/video/SDL_video.c	Sun Jul 22 19:42:08 2018 -0400
     4.2 +++ b/src/video/SDL_video.c	Thu Aug 02 16:03:47 2018 -0400
     4.3 @@ -1331,9 +1331,43 @@
     4.4  #define CREATE_FLAGS \
     4.5      (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED)
     4.6  
     4.7 +static SDL_INLINE SDL_bool
     4.8 +IsAcceptingDragAndDrop(void)
     4.9 +{
    4.10 +    if ((SDL_GetEventState(SDL_DROPFILE) == SDL_ENABLE) ||
    4.11 +        (SDL_GetEventState(SDL_DROPTEXT) == SDL_ENABLE)) {
    4.12 +        return SDL_TRUE;
    4.13 +    }
    4.14 +    return SDL_FALSE;
    4.15 +}
    4.16 +
    4.17 +/* prepare a newly-created window */
    4.18 +static SDL_INLINE void
    4.19 +PrepareDragAndDropSupport(SDL_Window *window)
    4.20 +{
    4.21 +    if (_this->AcceptDragAndDrop) {
    4.22 +        _this->AcceptDragAndDrop(window, IsAcceptingDragAndDrop());
    4.23 +    }
    4.24 +}
    4.25 +
    4.26 +/* toggle d'n'd for all existing windows. */
    4.27 +void
    4.28 +SDL_ToggleDragAndDropSupport(void)
    4.29 +{
    4.30 +    if (_this && _this->AcceptDragAndDrop) {
    4.31 +        const SDL_bool enable = IsAcceptingDragAndDrop();
    4.32 +        SDL_Window *window;
    4.33 +        for (window = _this->windows; window; window = window->next) {
    4.34 +            _this->AcceptDragAndDrop(window, enable);
    4.35 +        }
    4.36 +    }
    4.37 +}
    4.38 +
    4.39  static void
    4.40  SDL_FinishWindowCreation(SDL_Window *window, Uint32 flags)
    4.41  {
    4.42 +    PrepareDragAndDropSupport(window);
    4.43 +
    4.44      if (flags & SDL_WINDOW_MAXIMIZED) {
    4.45          SDL_MaximizeWindow(window);
    4.46      }
    4.47 @@ -1552,6 +1586,9 @@
    4.48          SDL_DestroyWindow(window);
    4.49          return NULL;
    4.50      }
    4.51 +
    4.52 +    PrepareDragAndDropSupport(window);
    4.53 +
    4.54      return window;
    4.55  }
    4.56  
     5.1 --- a/src/video/cocoa/SDL_cocoavideo.m	Sun Jul 22 19:42:08 2018 -0400
     5.2 +++ b/src/video/cocoa/SDL_cocoavideo.m	Thu Aug 02 16:03:47 2018 -0400
     5.3 @@ -105,6 +105,7 @@
     5.4      device->DestroyWindow = Cocoa_DestroyWindow;
     5.5      device->GetWindowWMInfo = Cocoa_GetWindowWMInfo;
     5.6      device->SetWindowHitTest = Cocoa_SetWindowHitTest;
     5.7 +    device->AcceptDragAndDrop = Cocoa_AcceptDragAndDrop;
     5.8  
     5.9      device->shape_driver.CreateShaper = Cocoa_CreateShaper;
    5.10      device->shape_driver.SetWindowShape = Cocoa_SetWindowShape;
     6.1 --- a/src/video/cocoa/SDL_cocoawindow.h	Sun Jul 22 19:42:08 2018 -0400
     6.2 +++ b/src/video/cocoa/SDL_cocoawindow.h	Thu Aug 02 16:03:47 2018 -0400
     6.3 @@ -148,6 +148,7 @@
     6.4  extern void Cocoa_DestroyWindow(_THIS, SDL_Window * window);
     6.5  extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info);
     6.6  extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
     6.7 +extern void Cocoa_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept);
     6.8  
     6.9  #endif /* SDL_cocoawindow_h_ */
    6.10  
     7.1 --- a/src/video/cocoa/SDL_cocoawindow.m	Sun Jul 22 19:42:08 2018 -0400
     7.2 +++ b/src/video/cocoa/SDL_cocoawindow.m	Thu Aug 02 16:03:47 2018 -0400
     7.3 @@ -1354,9 +1354,6 @@
     7.4      [nswindow setContentView:contentView];
     7.5      [contentView release];
     7.6  
     7.7 -    /* Allow files and folders to be dragged onto the window by users */
     7.8 -    [nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
     7.9 -
    7.10      if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
    7.11          [nswindow release];
    7.12          return -1;
    7.13 @@ -1864,6 +1861,17 @@
    7.14      return 0;  /* just succeed, the real work is done elsewhere. */
    7.15  }
    7.16  
    7.17 +void
    7.18 +Cocoa_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
    7.19 +{
    7.20 +    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    7.21 +    if (accept) {
    7.22 +        [data->nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
    7.23 +    } else {
    7.24 +        [data->nswindow unregisterDraggedTypes];
    7.25 +    }
    7.26 +}
    7.27 +
    7.28  int
    7.29  Cocoa_SetWindowOpacity(_THIS, SDL_Window * window, float opacity)
    7.30  {
     8.1 --- a/src/video/windows/SDL_windowsvideo.c	Sun Jul 22 19:42:08 2018 -0400
     8.2 +++ b/src/video/windows/SDL_windowsvideo.c	Thu Aug 02 16:03:47 2018 -0400
     8.3 @@ -164,6 +164,7 @@
     8.4      device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer;
     8.5      device->OnWindowEnter = WIN_OnWindowEnter;
     8.6      device->SetWindowHitTest = WIN_SetWindowHitTest;
     8.7 +    device->AcceptDragAndDrop = WIN_AcceptDragAndDrop;
     8.8  
     8.9      device->shape_driver.CreateShaper = Win32_CreateShaper;
    8.10      device->shape_driver.SetWindowShape = Win32_SetWindowShape;
     9.1 --- a/src/video/windows/SDL_windowswindow.c	Sun Jul 22 19:42:08 2018 -0400
     9.2 +++ b/src/video/windows/SDL_windowswindow.c	Thu Aug 02 16:03:47 2018 -0400
     9.3 @@ -289,9 +289,6 @@
     9.4          videodata->RegisterTouchWindow(hwnd, (TWF_FINETOUCH|TWF_WANTPALM));
     9.5      }
     9.6  
     9.7 -    /* Enable dropping files */
     9.8 -    DragAcceptFiles(hwnd, TRUE);
     9.9 -
    9.10      data->initializing = SDL_FALSE;
    9.11  
    9.12      /* All done! */
    9.13 @@ -979,6 +976,13 @@
    9.14      return 0;
    9.15  }
    9.16  
    9.17 +void
    9.18 +WIN_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
    9.19 +{
    9.20 +    const SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    9.21 +    DragAcceptFiles(data->hwnd, accept ? TRUE : FALSE);
    9.22 +}
    9.23 +
    9.24  #endif /* SDL_VIDEO_DRIVER_WINDOWS */
    9.25  
    9.26  /* vi: set ts=4 sw=4 expandtab: */
    10.1 --- a/src/video/windows/SDL_windowswindow.h	Sun Jul 22 19:42:08 2018 -0400
    10.2 +++ b/src/video/windows/SDL_windowswindow.h	Thu Aug 02 16:03:47 2018 -0400
    10.3 @@ -78,6 +78,7 @@
    10.4  extern void WIN_OnWindowEnter(_THIS, SDL_Window * window);
    10.5  extern void WIN_UpdateClipCursor(SDL_Window *window);
    10.6  extern int WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
    10.7 +extern void WIN_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept);
    10.8  
    10.9  #endif /* SDL_windowswindow_h_ */
   10.10  
    11.1 --- a/src/video/x11/SDL_x11video.c	Sun Jul 22 19:42:08 2018 -0400
    11.2 +++ b/src/video/x11/SDL_x11video.c	Thu Aug 02 16:03:47 2018 -0400
    11.3 @@ -260,6 +260,7 @@
    11.4      device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
    11.5      device->GetWindowWMInfo = X11_GetWindowWMInfo;
    11.6      device->SetWindowHitTest = X11_SetWindowHitTest;
    11.7 +    device->AcceptDragAndDrop = X11_AcceptDragAndDrop;
    11.8  
    11.9      device->shape_driver.CreateShaper = X11_CreateShaper;
   11.10      device->shape_driver.SetWindowShape = X11_SetWindowShape;
    12.1 --- a/src/video/x11/SDL_x11window.c	Sun Jul 22 19:42:08 2018 -0400
    12.2 +++ b/src/video/x11/SDL_x11window.c	Thu Aug 02 16:03:47 2018 -0400
    12.3 @@ -390,7 +390,6 @@
    12.4      const char *wintype_name = NULL;
    12.5      long compositor = 1;
    12.6      Atom _NET_WM_PID;
    12.7 -    Atom XdndAware, xdnd_version = 5;
    12.8      long fevent = 0;
    12.9  
   12.10  #if SDL_VIDEO_OPENGL_GLX || SDL_VIDEO_OPENGL_EGL
   12.11 @@ -651,11 +650,6 @@
   12.12                   PropertyChangeMask | StructureNotifyMask |
   12.13                   KeymapStateMask | fevent));
   12.14  
   12.15 -    XdndAware = X11_XInternAtom(display, "XdndAware", False);
   12.16 -    X11_XChangeProperty(display, w, XdndAware, XA_ATOM, 32,
   12.17 -                 PropModeReplace,
   12.18 -                 (unsigned char*)&xdnd_version, 1);
   12.19 -
   12.20      X11_XFlush(display);
   12.21  
   12.22      return 0;
   12.23 @@ -1604,6 +1598,22 @@
   12.24      return 0;  /* just succeed, the real work is done elsewhere. */
   12.25  }
   12.26  
   12.27 +void
   12.28 +X11_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
   12.29 +{
   12.30 +    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   12.31 +    Display *display = data->videodata->display;
   12.32 +    Atom XdndAware = X11_XInternAtom(display, "XdndAware", False);
   12.33 +
   12.34 +    if (accept) {
   12.35 +        Atom xdnd_version = 5;
   12.36 +        X11_XChangeProperty(display, data->xwindow, XdndAware, XA_ATOM, 32,
   12.37 +                     PropModeReplace, (unsigned char*)&xdnd_version, 1);
   12.38 +    } else {
   12.39 +        X11_XDeleteProperty(display, data->xwindow, XdndAware);
   12.40 +    }
   12.41 +}
   12.42 +
   12.43  #endif /* SDL_VIDEO_DRIVER_X11 */
   12.44  
   12.45  /* vi: set ts=4 sw=4 expandtab: */
    13.1 --- a/src/video/x11/SDL_x11window.h	Sun Jul 22 19:42:08 2018 -0400
    13.2 +++ b/src/video/x11/SDL_x11window.h	Thu Aug 02 16:03:47 2018 -0400
    13.3 @@ -104,6 +104,7 @@
    13.4  extern SDL_bool X11_GetWindowWMInfo(_THIS, SDL_Window * window,
    13.5                                      struct SDL_SysWMinfo *info);
    13.6  extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
    13.7 +extern void X11_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept);
    13.8  
    13.9  #endif /* SDL_x11window_h_ */
   13.10  
    14.1 --- a/test/testwm2.c	Sun Jul 22 19:42:08 2018 -0400
    14.2 +++ b/test/testwm2.c	Thu Aug 02 16:03:47 2018 -0400
    14.3 @@ -146,6 +146,9 @@
    14.4          quit(2);
    14.5      }
    14.6  
    14.7 +    SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
    14.8 +    SDL_EventState(SDL_DROPTEXT, SDL_ENABLE);
    14.9 +
   14.10      for (i = 0; i < state->num_windows; ++i) {
   14.11          SDL_Renderer *renderer = state->renderers[i];
   14.12          SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);