src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Fri, 23 Oct 2009 04:08:32 +0000
changeset 3414 1e45c3012a4f
parent 3409 c8f580ebc96a
child 3506 e829b6098435
permissions -rw-r--r--
Adam Strzelecki to SDL

I think something is wrong (look at the fix patch below):
(1) NSRect rect shouldn't be initialized with contentRectForFrameRect: at the top of the function, contentRectForFrameRect is called in non-fullscreen case anyway (1st @@)
(2) I think you've left two lines that should be removed completely (2nd @@)
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #include "SDL_syswm.h"
    25 #include "../SDL_sysvideo.h"
    26 #include "../../events/SDL_keyboard_c.h"
    27 #include "../../events/SDL_mouse_c.h"
    28 #include "../../events/SDL_windowevents_c.h"
    29 
    30 #include "SDL_cocoavideo.h"
    31 
    32 static __inline__ void ConvertNSRect(NSRect *r)
    33 {
    34     /* FIXME: Cache the display used for this window */
    35     r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
    36 }
    37 
    38 @implementation Cocoa_WindowListener
    39 
    40 - (void)listen:(SDL_WindowData *)data
    41 {
    42     NSNotificationCenter *center;
    43 
    44     _data = data;
    45 
    46     center = [NSNotificationCenter defaultCenter];
    47 
    48     [_data->window setNextResponder:self];
    49     if ([_data->window delegate] != nil) {
    50         [center addObserver:self selector:@selector(windowDisExpose:) name:NSWindowDidExposeNotification object:_data->window];
    51         [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:_data->window];
    52         [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:_data->window];
    53         [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:_data->window];
    54         [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:_data->window];
    55         [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:_data->window];
    56         [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:_data->window];
    57     } else {
    58         [_data->window setDelegate:self];
    59     }
    60     [center addObserver:self selector:@selector(windowDidHide:) name:NSApplicationDidHideNotification object:NSApp];
    61     [center addObserver:self selector:@selector(windowDidUnhide:) name:NSApplicationDidUnhideNotification object:NSApp];
    62 
    63     [_data->window setAcceptsMouseMovedEvents:YES];
    64 }
    65 
    66 - (void)close
    67 {
    68     NSNotificationCenter *center;
    69 
    70     center = [NSNotificationCenter defaultCenter];
    71 
    72     [_data->window setNextResponder:nil];
    73     if ([_data->window delegate] != self) {
    74         [center removeObserver:self name:NSWindowDidExposeNotification object:_data->window];
    75         [center removeObserver:self name:NSWindowDidMoveNotification object:_data->window];
    76         [center removeObserver:self name:NSWindowDidResizeNotification object:_data->window];
    77         [center removeObserver:self name:NSWindowDidMiniaturizeNotification object:_data->window];
    78         [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:_data->window];
    79         [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:_data->window];
    80         [center removeObserver:self name:NSWindowDidResignKeyNotification object:_data->window];
    81     } else {
    82         [_data->window setDelegate:nil];
    83     }
    84     [center removeObserver:self name:NSApplicationDidHideNotification object:NSApp];
    85     [center removeObserver:self name:NSApplicationDidUnhideNotification object:NSApp];
    86 }
    87 
    88 - (BOOL)windowShouldClose:(id)sender
    89 {
    90     SDL_SendWindowEvent(_data->windowID, SDL_WINDOWEVENT_CLOSE, 0, 0);
    91     return NO;
    92 }
    93 
    94 - (void)windowDidExpose:(NSNotification *)aNotification
    95 {
    96     SDL_SendWindowEvent(_data->windowID, SDL_WINDOWEVENT_EXPOSED, 0, 0);
    97 }
    98 
    99 - (void)windowDidMove:(NSNotification *)aNotification
   100 {
   101     int x, y;
   102     NSRect rect = [_data->window contentRectForFrameRect:[_data->window frame]];
   103     ConvertNSRect(&rect);
   104     x = (int)rect.origin.x;
   105     y = (int)rect.origin.y;
   106     SDL_SendWindowEvent(_data->windowID, SDL_WINDOWEVENT_MOVED, x, y);
   107 }
   108 
   109 - (void)windowDidResize:(NSNotification *)aNotification
   110 {
   111     int w, h;
   112     NSRect rect = [_data->window contentRectForFrameRect:[_data->window frame]];
   113     w = (int)rect.size.width;
   114     h = (int)rect.size.height;
   115     SDL_SendWindowEvent(_data->windowID, SDL_WINDOWEVENT_RESIZED, w, h);
   116 }
   117 
   118 - (void)windowDidMiniaturize:(NSNotification *)aNotification
   119 {
   120     SDL_SendWindowEvent(_data->windowID, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   121 }
   122 
   123 - (void)windowDidDeminiaturize:(NSNotification *)aNotification
   124 {
   125     SDL_SendWindowEvent(_data->windowID, SDL_WINDOWEVENT_RESTORED, 0, 0);
   126 }
   127 
   128 - (void)windowDidBecomeKey:(NSNotification *)aNotification
   129 {
   130     int index;
   131 
   132     /* We're going to get keyboard events, since we're key. */
   133     index = _data->videodata->keyboard;
   134     SDL_SetKeyboardFocus(index, _data->windowID);
   135 }
   136 
   137 - (void)windowDidResignKey:(NSNotification *)aNotification
   138 {
   139     int index;
   140     SDL_Mouse *mouse;
   141 
   142     /* Some other window will get mouse events, since we're not key. */
   143     index = _data->videodata->mouse;
   144     mouse = SDL_GetMouse(index);
   145     if (mouse->focus == _data->windowID) {
   146         SDL_SetMouseFocus(index, 0);
   147     }
   148 
   149     /* Some other window will get keyboard events, since we're not key. */
   150     index = _data->videodata->keyboard;
   151     SDL_SetKeyboardFocus(index, 0);
   152 }
   153 
   154 - (void)windowDidHide:(NSNotification *)aNotification
   155 {
   156     SDL_SendWindowEvent(_data->windowID, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   157 }
   158 
   159 - (void)windowDidUnhide:(NSNotification *)aNotification
   160 {
   161     SDL_SendWindowEvent(_data->windowID, SDL_WINDOWEVENT_SHOWN, 0, 0);
   162 }
   163 
   164 - (void)mouseDown:(NSEvent *)theEvent
   165 {
   166     int index;
   167     int button;
   168 
   169     index = _data->videodata->mouse;
   170     switch ([theEvent buttonNumber]) {
   171     case 0:
   172         button = SDL_BUTTON_LEFT;
   173         break;
   174     case 1:
   175         button = SDL_BUTTON_RIGHT;
   176         break;
   177     case 2:
   178         button = SDL_BUTTON_MIDDLE;
   179         break;
   180     default:
   181         button = [theEvent buttonNumber];
   182         break;
   183     }
   184     SDL_SendMouseButton(index, SDL_PRESSED, button);
   185 }
   186 
   187 - (void)rightMouseDown:(NSEvent *)theEvent
   188 {
   189     [self mouseDown:theEvent];
   190 }
   191 
   192 - (void)otherMouseDown:(NSEvent *)theEvent
   193 {
   194     [self mouseDown:theEvent];
   195 }
   196 
   197 - (void)mouseUp:(NSEvent *)theEvent
   198 {
   199     int index;
   200     int button;
   201 
   202     index = _data->videodata->mouse;
   203     switch ([theEvent buttonNumber]) {
   204     case 0:
   205         button = SDL_BUTTON_LEFT;
   206         break;
   207     case 1:
   208         button = SDL_BUTTON_RIGHT;
   209         break;
   210     case 2:
   211         button = SDL_BUTTON_MIDDLE;
   212         break;
   213     default:
   214         button = [theEvent buttonNumber];
   215         break;
   216     }
   217     SDL_SendMouseButton(index, SDL_RELEASED, button);
   218 }
   219 
   220 - (void)rightMouseUp:(NSEvent *)theEvent
   221 {
   222     [self mouseUp:theEvent];
   223 }
   224 
   225 - (void)otherMouseUp:(NSEvent *)theEvent
   226 {
   227     [self mouseUp:theEvent];
   228 }
   229 
   230 - (void)mouseMoved:(NSEvent *)theEvent
   231 {
   232     SDL_Window *window = SDL_GetWindowFromID(_data->windowID);
   233     int index;
   234     SDL_Mouse *mouse;
   235     NSPoint point;
   236     NSRect rect;
   237 
   238     index = _data->videodata->mouse;
   239     mouse = SDL_GetMouse(index);
   240 
   241     point = [NSEvent mouseLocation];
   242     if ( (window->flags & SDL_WINDOW_FULLSCREEN) ) {
   243         rect.size.width = CGDisplayPixelsWide(kCGDirectMainDisplay);
   244         rect.size.height = CGDisplayPixelsHigh(kCGDirectMainDisplay);
   245         point.x = point.x - rect.origin.x;
   246         point.y = rect.size.height - point.y;
   247     } else {
   248         rect = [_data->window contentRectForFrameRect:[_data->window frame]];
   249         point.y = rect.size.height - (point.y - rect.origin.y);
   250     }
   251     if ( point.x < 0 || point.x >= rect.size.width ||
   252          point.y < 0 || point.y >= rect.size.height ) {
   253         if (mouse->focus != 0) {
   254             SDL_SetMouseFocus(index, 0);
   255         }
   256     } else {
   257         if (mouse->focus != _data->windowID) {
   258             SDL_SetMouseFocus(index, _data->windowID);
   259         }
   260         SDL_SendMouseMotion(index, 0, (int)point.x, (int)point.y, 0);
   261     }
   262 }
   263 
   264 - (void)mouseDragged:(NSEvent *)theEvent
   265 {
   266     [self mouseMoved:theEvent];
   267 }
   268 
   269 - (void)rightMouseDragged:(NSEvent *)theEvent
   270 {
   271     [self mouseMoved:theEvent];
   272 }
   273 
   274 - (void)otherMouseDragged:(NSEvent *)theEvent
   275 {
   276     [self mouseMoved:theEvent];
   277 }
   278 
   279 - (void)scrollWheel:(NSEvent *)theEvent
   280 {
   281     int index;
   282 
   283     index = _data->videodata->mouse;
   284     SDL_SendMouseWheel(index, (int)([theEvent deltaX]+0.9f), (int)([theEvent deltaY]+0.9f));
   285 }
   286 
   287 @end
   288 
   289 @interface SDLWindow : NSWindow
   290 /* These are needed for borderless/fullscreen windows */
   291 - (BOOL)canBecomeKeyWindow;
   292 - (BOOL)canBecomeMainWindow;
   293 @end
   294 
   295 @implementation SDLWindow
   296 - (BOOL)canBecomeKeyWindow
   297 {
   298     return YES;
   299 }
   300 
   301 - (BOOL)canBecomeMainWindow
   302 {
   303     return YES;
   304 }
   305 @end
   306 
   307 static int
   308 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
   309 {
   310     NSAutoreleasePool *pool;
   311     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   312     SDL_WindowData *data;
   313 
   314     /* Allocate the window data */
   315     data = (SDL_WindowData *) SDL_malloc(sizeof(*data));
   316     if (!data) {
   317         SDL_OutOfMemory();
   318         return -1;
   319     }
   320     data->windowID = window->id;
   321     data->window = nswindow;
   322     data->created = created;
   323     data->videodata = videodata;
   324 
   325     pool = [[NSAutoreleasePool alloc] init];
   326 
   327     /* Create an event listener for the window */
   328     data->listener = [[Cocoa_WindowListener alloc] init];
   329     [data->listener listen:data];
   330 
   331     /* Fill in the SDL window with the window data */
   332     {
   333         NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   334         ConvertNSRect(&rect);
   335         window->x = (int)rect.origin.x;
   336         window->y = (int)rect.origin.y;
   337         window->w = (int)rect.size.width;
   338         window->h = (int)rect.size.height;
   339     }
   340     if ([nswindow isVisible]) {
   341         window->flags |= SDL_WINDOW_SHOWN;
   342     } else {
   343         window->flags &= ~SDL_WINDOW_SHOWN;
   344     }
   345     {
   346         unsigned int style = [nswindow styleMask];
   347 
   348         if ((style & ~NSResizableWindowMask) == NSBorderlessWindowMask) {
   349             window->flags |= SDL_WINDOW_BORDERLESS;
   350         } else {
   351             window->flags &= ~SDL_WINDOW_BORDERLESS;
   352         }
   353         if (style & NSResizableWindowMask) {
   354             window->flags |= SDL_WINDOW_RESIZABLE;
   355         } else {
   356             window->flags &= ~SDL_WINDOW_RESIZABLE;
   357         }
   358     }
   359     if ([nswindow isZoomed]) {
   360         window->flags |= SDL_WINDOW_MAXIMIZED;
   361     } else {
   362         window->flags &= ~SDL_WINDOW_MAXIMIZED;
   363     }
   364     if ([nswindow isMiniaturized]) {
   365         window->flags |= SDL_WINDOW_MINIMIZED;
   366     } else {
   367         window->flags &= ~SDL_WINDOW_MINIMIZED;
   368     }
   369     if ([nswindow isKeyWindow]) {
   370         int index = data->videodata->keyboard;
   371         window->flags |= SDL_WINDOW_INPUT_FOCUS;
   372         SDL_SetKeyboardFocus(index, data->windowID);
   373 
   374         if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
   375             /* FIXME */
   376         }
   377     }
   378 
   379     /* All done! */
   380     [pool release];
   381     window->driverdata = data;
   382     return 0;
   383 }
   384 
   385 int
   386 Cocoa_CreateWindow(_THIS, SDL_Window * window)
   387 {
   388     NSAutoreleasePool *pool;
   389     NSWindow *nswindow;
   390     NSRect rect;
   391     unsigned int style;
   392     NSString *title;
   393     int status;
   394 
   395     pool = [[NSAutoreleasePool alloc] init];
   396 
   397     if ((window->flags & SDL_WINDOW_FULLSCREEN)
   398         || window->x == SDL_WINDOWPOS_CENTERED) {
   399         rect.origin.x = (CGDisplayPixelsWide(kCGDirectMainDisplay) - window->w) / 2;
   400     } else if (window->x == SDL_WINDOWPOS_UNDEFINED) {
   401         rect.origin.x = 0;
   402     } else {
   403         rect.origin.x = window->x;
   404     }
   405     if ((window->flags & SDL_WINDOW_FULLSCREEN)
   406         || window->y == SDL_WINDOWPOS_CENTERED) {
   407         rect.origin.y = (CGDisplayPixelsHigh(kCGDirectMainDisplay) - window->h) / 2;
   408     } else if (window->y == SDL_WINDOWPOS_UNDEFINED) {
   409         rect.origin.y = 0;
   410     } else {
   411         rect.origin.y = window->y;
   412     }
   413     rect.size.width = window->w;
   414     rect.size.height = window->h;
   415     ConvertNSRect(&rect);
   416 
   417     if (window->flags & SDL_WINDOW_BORDERLESS) {
   418         style = NSBorderlessWindowMask;
   419     } else {
   420         style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask);
   421     }
   422     if (window->flags & SDL_WINDOW_RESIZABLE) {
   423         style |= NSResizableWindowMask;
   424     }
   425 
   426     nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:FALSE];
   427 
   428     [pool release];
   429 
   430     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
   431         [nswindow release];
   432         return -1;
   433     }
   434     return 0;
   435 }
   436 
   437 int
   438 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   439 {
   440     NSAutoreleasePool *pool;
   441     NSWindow *nswindow = (NSWindow *) data;
   442     NSString *title;
   443     int status;
   444 
   445     pool = [[NSAutoreleasePool alloc] init];
   446 
   447     /* Query the title from the existing window */
   448     title = [nswindow title];
   449     if (title) {
   450         window->title = SDL_strdup([title UTF8String]);
   451     }
   452 
   453     [pool release];
   454 
   455     return SetupWindowData(_this, window, nswindow, SDL_FALSE);
   456 }
   457 
   458 void
   459 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
   460 {
   461     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   462     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->window;
   463     NSString *string;
   464 
   465     if(window->title) {
   466         string = [[NSString alloc] initWithUTF8String:window->title];
   467     } else {
   468         string = [[NSString alloc] init];
   469     }
   470     [nswindow setTitle:string];
   471     [string release];
   472 
   473     [pool release];
   474 }
   475 
   476 void
   477 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
   478 {
   479     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   480     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->window;
   481     NSRect rect;
   482 
   483     if ((window->flags & SDL_WINDOW_FULLSCREEN)
   484         || window->x == SDL_WINDOWPOS_CENTERED) {
   485         rect.origin.x = (CGDisplayPixelsWide(kCGDirectMainDisplay) - window->w) / 2;
   486     } else {
   487         rect.origin.x = window->x;
   488     }
   489     if ((window->flags & SDL_WINDOW_FULLSCREEN)
   490         || window->y == SDL_WINDOWPOS_CENTERED) {
   491         rect.origin.y = (CGDisplayPixelsHigh(kCGDirectMainDisplay) - window->h) / 2;
   492     } else {
   493         rect.origin.y = window->y;
   494     }
   495     rect.size.width = window->w;
   496     rect.size.height = window->h;
   497     ConvertNSRect(&rect);
   498     rect = [nswindow frameRectForContentRect:rect];
   499     [nswindow setFrameOrigin:rect.origin];
   500     [pool release];
   501 }
   502 
   503 void
   504 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
   505 {
   506     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   507     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->window;
   508     NSSize size;
   509 
   510     size.width = window->w;
   511     size.height = window->h;
   512     [nswindow setContentSize:size];
   513     [pool release];
   514 }
   515 
   516 void
   517 Cocoa_ShowWindow(_THIS, SDL_Window * window)
   518 {
   519     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   520     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->window;
   521 
   522     if (![nswindow isMiniaturized]) {
   523         [nswindow makeKeyAndOrderFront:nil];
   524     }
   525     [pool release];
   526 }
   527 
   528 void
   529 Cocoa_HideWindow(_THIS, SDL_Window * window)
   530 {
   531     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   532     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->window;
   533 
   534     [nswindow orderOut:nil];
   535     [pool release];
   536 }
   537 
   538 void
   539 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
   540 {
   541     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   542     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->window;
   543 
   544     [nswindow makeKeyAndOrderFront:nil];
   545     [pool release];
   546 }
   547 
   548 void
   549 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
   550 {
   551     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   552     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->window;
   553 
   554     [nswindow zoom:nil];
   555     [pool release];
   556 }
   557 
   558 void
   559 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
   560 {
   561     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   562     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->window;
   563 
   564     [nswindow miniaturize:nil];
   565     [pool release];
   566 }
   567 
   568 void
   569 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
   570 {
   571     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   572     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->window;
   573 
   574     if ([nswindow isMiniaturized]) {
   575         [nswindow deminiaturize:nil];
   576     } else if ([nswindow isZoomed]) {
   577         [nswindow zoom:nil];
   578     }
   579     [pool release];
   580 }
   581 
   582 void
   583 Cocoa_SetWindowGrab(_THIS, SDL_Window * window)
   584 {
   585     if ((window->flags & SDL_WINDOW_INPUT_GRABBED) &&
   586         (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
   587         /* FIXME: Grab mouse */
   588     } else {
   589         /* FIXME: Release mouse */
   590     }
   591 }
   592 
   593 void
   594 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
   595 {
   596     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   597     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   598 
   599     if (data) {
   600         [data->listener close];
   601         [data->listener release];
   602         if (data->created) {
   603             [data->window close];
   604         }
   605         SDL_free(data);
   606     }
   607     [pool release];
   608 }
   609 
   610 SDL_bool
   611 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   612 {
   613     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->window;
   614 
   615     if (info->version.major <= SDL_MAJOR_VERSION) {
   616         //info->window = nswindow;
   617         return SDL_TRUE;
   618     } else {
   619         SDL_SetError("Application not compiled with SDL %d.%d\n",
   620                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
   621         return SDL_FALSE;
   622     }
   623 }
   624 
   625 /* vi: set ts=4 sw=4 expandtab: */