First shot at SDL_SetWindowDragAreas().
authorRyan C. Gordon <icculus@icculus.org>
Tue, 27 May 2014 01:27:42 -0400
changeset 893144d8a2f4b431
parent 8930 d000e6339d41
child 8932 7eacbfcbb313
First shot at SDL_SetWindowDragAreas().

Only Cocoa implemented right now.
.hgignore
include/SDL_video.h
src/dynapi/SDL_dynapi_overrides.h
src/dynapi/SDL_dynapi_procs.h
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
test/Makefile.in
test/testdragareas.c
     1.1 --- a/.hgignore	Tue May 27 00:26:47 2014 -0400
     1.2 +++ b/.hgignore	Tue May 27 01:27:42 2014 -0400
     1.3 @@ -55,6 +55,7 @@
     1.4  test/testatomic
     1.5  test/testaudioinfo
     1.6  test/testautomation
     1.7 +test/testdragareas
     1.8  test/testdraw2
     1.9  test/testerror
    1.10  test/testfile
     2.1 --- a/include/SDL_video.h	Tue May 27 00:26:47 2014 -0400
     2.2 +++ b/include/SDL_video.h	Tue May 27 01:27:42 2014 -0400
     2.3 @@ -792,6 +792,44 @@
     2.4                                                     Uint16 * blue);
     2.5  
     2.6  /**
     2.7 + *  \brief Define regions of a window that can be used to drag it.
     2.8 + *
     2.9 + *  Normally windows are dragged by decorations provided by the system
    2.10 + *  window manager (usually, a title bar), but for some apps, it makes sense
    2.11 + *  to drag them from somewhere else inside the window itself; for example,
    2.12 + *  one might have a borderless window that wants to be draggable from any
    2.13 + *  part, or simulate its own title bar, etc.
    2.14 + *
    2.15 + *  This method designates pieces of a given window as "drag areas," which
    2.16 + *  will move the window when the user drags with his mouse, as if she had
    2.17 + *  used the titlebar.
    2.18 + *
    2.19 + *  You may specify multiple drag areas, disconnected or overlapping. This
    2.20 + *  function accepts an array of rectangles. Each call to this function will
    2.21 + *  replace any previously-defined drag areas. To disable drag areas on a
    2.22 + *  window, call this function with a NULL array of zero elements.
    2.23 + *
    2.24 + *  Drag areas do not automatically resize. If your window changes dimensions
    2.25 + *  you should plan to re-call this function with new drag areas if
    2.26 + *  appropriate.
    2.27 + *
    2.28 + *  Mouse input may not be delivered to your application if it is within
    2.29 + *  a drag area; the OS will often apply that input to moving the window and
    2.30 + *  not deliver it to the application.
    2.31 + *
    2.32 + *  Platforms that don't support this functionality will return -1
    2.33 + *  unconditionally, even if you're attempting to disable drag areas.
    2.34 + *
    2.35 + *  \param window The window to set drag areas on.
    2.36 + *  \param areas An array of SDL_Rects containing num_areas elements.
    2.37 + *  \param num_areas The number of elements in the areas parameter.
    2.38 + *  \return 0 on success, -1 on error (including unsupported).
    2.39 + */
    2.40 +extern DECLSPEC int SDLCALL SDL_SetWindowDragAreas(SDL_Window * window,
    2.41 +                                                   const SDL_Rect *areas,
    2.42 +                                                   int num_areas);
    2.43 +
    2.44 +/**
    2.45   *  \brief Destroy a window.
    2.46   */
    2.47  extern DECLSPEC void SDLCALL SDL_DestroyWindow(SDL_Window * window);
     3.1 --- a/src/dynapi/SDL_dynapi_overrides.h	Tue May 27 00:26:47 2014 -0400
     3.2 +++ b/src/dynapi/SDL_dynapi_overrides.h	Tue May 27 01:27:42 2014 -0400
     3.3 @@ -580,3 +580,4 @@
     3.4  #define SDL_WinRTGetFSPathUTF8 SDL_WinRTGetFSPathUTF8_REAL
     3.5  #define SDL_WinRTRunApp SDL_WinRTRunApp_REAL
     3.6  #define SDL_CaptureMouse SDL_CaptureMouse_REAL
     3.7 +#define SDL_SetWindowDragAreas SDL_SetWindowDragAreas_REAL
     4.1 --- a/src/dynapi/SDL_dynapi_procs.h	Tue May 27 00:26:47 2014 -0400
     4.2 +++ b/src/dynapi/SDL_dynapi_procs.h	Tue May 27 01:27:42 2014 -0400
     4.3 @@ -613,3 +613,4 @@
     4.4  SDL_DYNAPI_PROC(int,SDL_WinRTRunApp,(int a, char **b, void *c),(a,b,c),return)
     4.5  #endif
     4.6  SDL_DYNAPI_PROC(int,SDL_CaptureMouse,(SDL_bool a),(a),return)
     4.7 +SDL_DYNAPI_PROC(int,SDL_SetWindowDragAreas,(SDL_Window *a, const SDL_Rect *b, int c),(a,b,c),return)
     5.1 --- a/src/video/SDL_sysvideo.h	Tue May 27 00:26:47 2014 -0400
     5.2 +++ b/src/video/SDL_sysvideo.h	Tue May 27 01:27:42 2014 -0400
     5.3 @@ -97,6 +97,9 @@
     5.4  
     5.5      SDL_WindowShaper *shaper;
     5.6  
     5.7 +    int num_drag_areas;
     5.8 +    SDL_Rect *drag_areas;
     5.9 +
    5.10      SDL_WindowUserData *data;
    5.11  
    5.12      void *driverdata;
    5.13 @@ -261,6 +264,9 @@
    5.14      /* MessageBox */
    5.15      int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid);
    5.16  
    5.17 +    /* Drag areas. Note that (areas) and (num_areas) are also copied to the SDL_Window for you after this call. */
    5.18 +    int (*SetWindowDragAreas)(SDL_Window * window, const SDL_Rect *areas, int num_areas);
    5.19 +
    5.20      /* * * */
    5.21      /* Data common to all drivers */
    5.22      SDL_bool suspend_screensaver;
     6.1 --- a/src/video/SDL_video.c	Tue May 27 00:26:47 2014 -0400
     6.2 +++ b/src/video/SDL_video.c	Tue May 27 01:27:42 2014 -0400
     6.3 @@ -1410,6 +1410,11 @@
     6.4          SDL_SetWindowIcon(window, icon);
     6.5          SDL_FreeSurface(icon);
     6.6      }
     6.7 +
     6.8 +    if (window->num_drag_areas > 0) {
     6.9 +        _this->SetWindowDragAreas(window, window->drag_areas, window->num_drag_areas);
    6.10 +    }
    6.11 +
    6.12      SDL_FinishWindowCreation(window, flags);
    6.13  
    6.14      return 0;
    6.15 @@ -2305,6 +2310,8 @@
    6.16          _this->windows = window->next;
    6.17      }
    6.18  
    6.19 +    SDL_free(window->drag_areas);
    6.20 +
    6.21      SDL_free(window);
    6.22  }
    6.23  
    6.24 @@ -3380,4 +3387,34 @@
    6.25      return SDL_TRUE;
    6.26  }
    6.27  
    6.28 +int
    6.29 +SDL_SetWindowDragAreas(SDL_Window * window, const SDL_Rect *_areas, int num_areas)
    6.30 +{
    6.31 +    SDL_Rect *areas = NULL;
    6.32 +
    6.33 +    CHECK_WINDOW_MAGIC(window, -1);
    6.34 +
    6.35 +    if (!_this->SetWindowDragAreas) {
    6.36 +        return SDL_Unsupported();
    6.37 +    }
    6.38 +
    6.39 +    if (num_areas > 0) {
    6.40 +        const size_t len = sizeof (SDL_Rect) * num_areas;
    6.41 +        areas = (SDL_Rect *) SDL_malloc(len);
    6.42 +        if (!areas) {
    6.43 +            return SDL_OutOfMemory();
    6.44 +        }
    6.45 +        SDL_memcpy(areas, _areas, len);
    6.46 +    }
    6.47 +
    6.48 +    if (_this->SetWindowDragAreas(window, areas, num_areas) == -1) {
    6.49 +        SDL_free(areas);
    6.50 +        return -1;
    6.51 +    }
    6.52 +
    6.53 +    SDL_free(window->drag_areas);
    6.54 +    window->drag_areas = areas;
    6.55 +    window->num_drag_areas = num_areas;
    6.56 +}
    6.57 +
    6.58  /* vi: set ts=4 sw=4 expandtab: */
     7.1 --- a/src/video/cocoa/SDL_cocoavideo.m	Tue May 27 00:26:47 2014 -0400
     7.2 +++ b/src/video/cocoa/SDL_cocoavideo.m	Tue May 27 01:27:42 2014 -0400
     7.3 @@ -108,6 +108,7 @@
     7.4      device->SetWindowGrab = Cocoa_SetWindowGrab;
     7.5      device->DestroyWindow = Cocoa_DestroyWindow;
     7.6      device->GetWindowWMInfo = Cocoa_GetWindowWMInfo;
     7.7 +    device->SetWindowDragAreas = Cocoa_SetWindowDragAreas;
     7.8  
     7.9      device->shape_driver.CreateShaper = Cocoa_CreateShaper;
    7.10      device->shape_driver.SetWindowShape = Cocoa_SetWindowShape;
     8.1 --- a/src/video/cocoa/SDL_cocoawindow.h	Tue May 27 00:26:47 2014 -0400
     8.2 +++ b/src/video/cocoa/SDL_cocoawindow.h	Tue May 27 01:27:42 2014 -0400
     8.3 @@ -45,6 +45,7 @@
     8.4      PendingWindowOperation pendingWindowOperation;
     8.5      BOOL isMoving;
     8.6      int pendingWindowWarpX, pendingWindowWarpY;
     8.7 +    BOOL isDragAreaRunning;
     8.8  }
     8.9  
    8.10  -(void) listen:(SDL_WindowData *) data;
    8.11 @@ -75,6 +76,9 @@
    8.12  -(void) windowDidExitFullScreen:(NSNotification *) aNotification;
    8.13  -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
    8.14  
    8.15 +/* See if event is in a drag area, toggle on window dragging. */
    8.16 +-(BOOL) processDragArea:(NSEvent *)theEvent;
    8.17 +
    8.18  /* Window event handling */
    8.19  -(void) mouseDown:(NSEvent *) theEvent;
    8.20  -(void) rightMouseDown:(NSEvent *) theEvent;
    8.21 @@ -115,6 +119,7 @@
    8.22      SDL_bool inWindowMove;
    8.23      Cocoa_WindowListener *listener;
    8.24      struct SDL_VideoData *videodata;
    8.25 +    NSView *dragarea;
    8.26  };
    8.27  
    8.28  extern int Cocoa_CreateWindow(_THIS, SDL_Window * window);
    8.29 @@ -138,8 +143,8 @@
    8.30  extern int Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp);
    8.31  extern void Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
    8.32  extern void Cocoa_DestroyWindow(_THIS, SDL_Window * window);
    8.33 -extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window,
    8.34 -                                      struct SDL_SysWMinfo *info);
    8.35 +extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info);
    8.36 +extern int Cocoa_SetWindowDragAreas(SDL_Window *window, const SDL_Rect *areas, int num_areas);
    8.37  
    8.38  #endif /* _SDL_cocoawindow_h */
    8.39  
     9.1 --- a/src/video/cocoa/SDL_cocoawindow.m	Tue May 27 00:26:47 2014 -0400
     9.2 +++ b/src/video/cocoa/SDL_cocoawindow.m	Tue May 27 01:27:42 2014 -0400
     9.3 @@ -180,6 +180,7 @@
     9.4      inFullscreenTransition = NO;
     9.5      pendingWindowOperation = PENDING_OPERATION_NONE;
     9.6      isMoving = NO;
     9.7 +    isDragAreaRunning = NO;
     9.8  
     9.9      center = [NSNotificationCenter defaultCenter];
    9.10  
    9.11 @@ -656,10 +657,46 @@
    9.12      /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
    9.13  }
    9.14  
    9.15 +- (BOOL)processDragArea:(NSEvent *)theEvent
    9.16 +{
    9.17 +    const int num_areas = _data->window->num_drag_areas;
    9.18 +
    9.19 +    SDL_assert(isDragAreaRunning == [_data->nswindow isMovableByWindowBackground]);
    9.20 +    SDL_assert((num_areas > 0) || !isDragAreaRunning);
    9.21 +
    9.22 +    if (num_areas > 0) {  /* if no drag areas, skip this. */
    9.23 +        int i;
    9.24 +        const NSPoint location = [theEvent locationInWindow];
    9.25 +        const SDL_Point point = { (int) location.x, _data->window->h - (((int) location.y)-1) };
    9.26 +        const SDL_Rect *areas = _data->window->drag_areas;
    9.27 +        for (i = 0; i < num_areas; i++) {
    9.28 +            if (SDL_PointInRect(&point, &areas[i])) {
    9.29 +                if (!isDragAreaRunning) {
    9.30 +                    isDragAreaRunning = YES;
    9.31 +                    [_data->nswindow setMovableByWindowBackground:YES];
    9.32 +                }
    9.33 +                return YES;  /* started a new drag! */
    9.34 +            }
    9.35 +        }
    9.36 +    }
    9.37 +
    9.38 +    if (isDragAreaRunning) {
    9.39 +        isDragAreaRunning = NO;
    9.40 +        [_data->nswindow setMovableByWindowBackground:NO];
    9.41 +        return YES;  /* was dragging, drop event. */
    9.42 +    }
    9.43 +
    9.44 +    return NO;  /* not a drag area, carry on. */
    9.45 +}
    9.46 +
    9.47  - (void)mouseDown:(NSEvent *)theEvent
    9.48  {
    9.49      int button;
    9.50  
    9.51 +    if ([self processDragArea:theEvent]) {
    9.52 +        return;  /* dragging, drop event. */
    9.53 +    }
    9.54 +
    9.55      switch ([theEvent buttonNumber]) {
    9.56      case 0:
    9.57          if (([theEvent modifierFlags] & NSControlKeyMask) &&
    9.58 @@ -698,6 +735,10 @@
    9.59  {
    9.60      int button;
    9.61  
    9.62 +    if ([self processDragArea:theEvent]) {
    9.63 +        return;  /* stopped dragging, drop event. */
    9.64 +    }
    9.65 +
    9.66      switch ([theEvent buttonNumber]) {
    9.67      case 0:
    9.68          if (wasCtrlLeft) {
    9.69 @@ -737,6 +778,10 @@
    9.70      NSPoint point;
    9.71      int x, y;
    9.72  
    9.73 +    if ([self processDragArea:theEvent]) {
    9.74 +        return;  /* dragging, drop event. */
    9.75 +    }
    9.76 +
    9.77      if (mouse->relative_mode) {
    9.78          return;
    9.79      }
    9.80 @@ -883,6 +928,7 @@
    9.81  
    9.82  /* The default implementation doesn't pass rightMouseDown to responder chain */
    9.83  - (void)rightMouseDown:(NSEvent *)theEvent;
    9.84 +- (BOOL)mouseDownCanMoveWindow;
    9.85  @end
    9.86  
    9.87  @implementation SDLView
    9.88 @@ -891,6 +937,14 @@
    9.89      [[self nextResponder] rightMouseDown:theEvent];
    9.90  }
    9.91  
    9.92 +- (BOOL)mouseDownCanMoveWindow
    9.93 +{
    9.94 +    /* Always say YES, but this doesn't do anything until we call
    9.95 +       -[NSWindow setMovableByWindowBackground:YES], which we ninja-toggle
    9.96 +       during mouse events when we're using a drag area. */
    9.97 +    return YES;
    9.98 +}
    9.99 +
   9.100  - (void)resetCursorRects
   9.101  {
   9.102      [super resetCursorRects];
   9.103 @@ -1544,6 +1598,12 @@
   9.104      return succeeded;
   9.105  }
   9.106  
   9.107 +int
   9.108 +Cocoa_SetWindowDragAreas(SDL_Window * window, const SDL_Rect *areas, int num_areas)
   9.109 +{
   9.110 +    return 0;  /* just succeed, the real work is done elsewhere. */
   9.111 +}
   9.112 +
   9.113  #endif /* SDL_VIDEO_DRIVER_COCOA */
   9.114  
   9.115  /* vi: set ts=4 sw=4 expandtab: */
    10.1 --- a/test/Makefile.in	Tue May 27 00:26:47 2014 -0400
    10.2 +++ b/test/Makefile.in	Tue May 27 01:27:42 2014 -0400
    10.3 @@ -12,6 +12,7 @@
    10.4  	loopwave$(EXE) \
    10.5  	testaudioinfo$(EXE) \
    10.6  	testautomation$(EXE) \
    10.7 +	testdragareas$(EXE) \
    10.8  	testdraw2$(EXE) \
    10.9  	testdrawchessboard$(EXE) \
   10.10  	testdropfile$(EXE) \
   10.11 @@ -108,6 +109,9 @@
   10.12  testrelative$(EXE): $(srcdir)/testrelative.c
   10.13  	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
   10.14  
   10.15 +testdragareas$(EXE): $(srcdir)/testdragareas.c
   10.16 +	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
   10.17 +
   10.18  testdraw2$(EXE): $(srcdir)/testdraw2.c
   10.19  	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
   10.20  
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/test/testdragareas.c	Tue May 27 01:27:42 2014 -0400
    11.3 @@ -0,0 +1,93 @@
    11.4 +#include <stdio.h>
    11.5 +#include "SDL.h"
    11.6 +
    11.7 +/* !!! FIXME: rewrite this to be wired in to test framework. */
    11.8 +
    11.9 +int main(int argc, char **argv)
   11.10 +{
   11.11 +    int done = 0;
   11.12 +    SDL_Window *window;
   11.13 +    SDL_Renderer *renderer;
   11.14 +
   11.15 +    const SDL_Rect drag_areas[] = {
   11.16 +        { 20, 20, 100, 100 },
   11.17 +        { 200, 70, 100, 100 },
   11.18 +        { 400, 90, 100, 100 }
   11.19 +    };
   11.20 +
   11.21 +    const SDL_Rect *areas = drag_areas;
   11.22 +    int numareas = SDL_arraysize(drag_areas);
   11.23 +
   11.24 +    /* !!! FIXME: check for errors. */
   11.25 +    SDL_Init(SDL_INIT_VIDEO);
   11.26 +    window = SDL_CreateWindow("Drag the red boxes", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_BORDERLESS);
   11.27 +    renderer = SDL_CreateRenderer(window, -1, 0);
   11.28 +
   11.29 +    if (SDL_SetWindowDragAreas(window, areas, numareas) == -1) {
   11.30 +        fprintf(stderr, "Setting drag areas failed!\n");
   11.31 +        SDL_Quit();
   11.32 +        return 1;
   11.33 +    }
   11.34 +
   11.35 +    while (!done)
   11.36 +    {
   11.37 +        SDL_SetRenderDrawColor(renderer, 0, 0, 127, 255);
   11.38 +        SDL_RenderClear(renderer);
   11.39 +        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
   11.40 +        SDL_RenderFillRects(renderer, areas, SDL_arraysize(drag_areas));
   11.41 +        SDL_RenderPresent(renderer);
   11.42 +
   11.43 +        SDL_Event e;
   11.44 +        int nothing_to_do = 1;
   11.45 +        while (SDL_PollEvent(&e)) {
   11.46 +            nothing_to_do = 0;
   11.47 +
   11.48 +            switch (e.type)
   11.49 +            {
   11.50 +                case SDL_MOUSEBUTTONDOWN:
   11.51 +                    printf("button down!\n");
   11.52 +                    break;
   11.53 +
   11.54 +                case SDL_MOUSEBUTTONUP:
   11.55 +                    printf("button up!\n");
   11.56 +                    break;
   11.57 +
   11.58 +                case SDL_WINDOWEVENT:
   11.59 +                    if (e.window.event == SDL_WINDOWEVENT_MOVED) {
   11.60 +                        printf("Window event moved to (%d, %d)!\n", (int) e.window.data1, (int) e.window.data2);
   11.61 +                    }
   11.62 +                    break;
   11.63 +
   11.64 +                case SDL_KEYDOWN:
   11.65 +                    if (e.key.keysym.sym == SDLK_ESCAPE) {
   11.66 +                        done = 1;
   11.67 +                    } else if (e.key.keysym.sym == SDLK_x) {
   11.68 +                        if (!areas) {
   11.69 +                            areas = drag_areas;
   11.70 +                            numareas = SDL_arraysize(drag_areas);
   11.71 +                        } else {
   11.72 +                            areas = NULL;
   11.73 +                            numareas = 0;
   11.74 +                        }
   11.75 +                        if (SDL_SetWindowDragAreas(window, areas, numareas) == -1) {
   11.76 +                            fprintf(stderr, "Setting drag areas failed!\n");
   11.77 +                            SDL_Quit();
   11.78 +                            return 1;
   11.79 +                        }
   11.80 +                    }
   11.81 +                    break;
   11.82 +
   11.83 +                case SDL_QUIT:
   11.84 +                    done = 1;
   11.85 +                    break;
   11.86 +            }
   11.87 +        }
   11.88 +
   11.89 +        if (nothing_to_do) {
   11.90 +            SDL_Delay(50);
   11.91 +        }
   11.92 +    }
   11.93 +
   11.94 +    SDL_Quit();
   11.95 +    return 0;
   11.96 +}