]> git.sesse.net Git - vlc/blob - plugins/beos/intf_beos.cpp
Added DVD device selection from interface menu
[vlc] / plugins / beos / intf_beos.cpp
1 /*****************************************************************************
2  * intf_beos.cpp: beos interface
3  *****************************************************************************
4  * Copyright (C) 1999, 2000, 2001 VideoLAN
5  * $Id: intf_beos.cpp,v 1.18 2001/03/06 19:52:03 richards Exp $
6  *
7  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Tony Castley <tcastley@mail.powerup.com.au>
10  *          Richard Shepherd <richard@rshepherd.demon.co.uk>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 #define MODULE_NAME beos
28 #include "modules_inner.h"
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #include "defs.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>                                      /* malloc(), free() */
37
38 #include <kernel/OS.h>
39 #include <storage/Path.h>
40 #include <Alert.h>
41 #include <View.h>
42 #include <CheckBox.h>
43 #include <Button.h>
44 #include <Slider.h>
45 #include <StatusBar.h>
46 #include <Application.h>
47 #include <Message.h>
48 #include <NodeInfo.h>
49 #include <Locker.h>
50 #include <DirectWindow.h>
51 #include <Box.h>
52 #include <Alert.h>
53 #include <MenuBar.h>
54 #include <MenuItem.h>
55 #include <FilePanel.h>
56 #include <Screen.h>
57 #include <malloc.h>
58 #include <string.h>
59 #include <Directory.h>
60 #include <Entry.h>
61 #include <Path.h>
62 #include <StorageDefs.h>
63 #include <scsi.h>
64 #include <scsiprobe_driver.h>
65
66 extern "C"
67 {
68 #include "config.h"
69 #include "common.h"
70 #include "threads.h"
71 #include "mtime.h"
72 #include "tests.h"
73 #include "modules.h"
74
75 #include "stream_control.h"
76 #include "input_ext-intf.h"
77
78 #include "interface.h"
79 #include "intf_plst.h"
80 #include "intf_msg.h"
81 #include "audio_output.h"
82 #include "MsgVals.h"
83
84
85 #include "main.h"
86 }
87
88 #include "InterfaceWindow.h"
89 #include "Bitmaps.h"
90 #include "TransportButton.h"
91
92 /*****************************************************************************
93  * intf_sys_t: description and status of FB interface
94  *****************************************************************************/
95 typedef struct intf_sys_s
96 {
97     InterfaceWindow * p_window;
98     char              i_key;
99 } intf_sys_t;
100
101 /*****************************************************************************
102  * InterfaceWindow
103  *****************************************************************************/
104  
105 InterfaceWindow::InterfaceWindow( BRect frame, const char *name , intf_thread_t  *p_interface )
106     : BWindow(frame, name, B_FLOATING_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
107         B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK |B_ASYNCHRONOUS_CONTROLS)
108 {
109     file_panel = NULL;
110     p_intf = p_interface;
111         BRect ButtonRect;
112         float xStart = 5.0;
113         float yStart = 20.0;
114
115     SetName( "interface" );
116     SetTitle(VOUT_TITLE " (BeOS interface)");
117     BRect rect(0, 0, 0, 0);
118     
119     BMenuBar *menu_bar; 
120     menu_bar = new BMenuBar(rect, "main menu");
121     AddChild( menu_bar );
122
123         BMenu *m, *cd_menu;
124
125         menu_bar->AddItem( m = new BMenu("File") );
126         menu_bar->ResizeToPreferred();
127         m->AddItem( new BMenuItem("Open File...", new BMessage(OPEN_FILE), 'O'));
128         cd_menu = new BMenu("Open DVD");
129         GetCD("/dev/disk", cd_menu);
130         m->AddItem(cd_menu);
131         m->AddSeparatorItem();
132         m->AddItem( new BMenuItem("About...", new BMessage(B_ABOUT_REQUESTED), 'A'));
133         m->AddItem( new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q'));
134         
135
136     rect = Bounds();
137     rect.top += menu_bar->Bounds().IntegerHeight()+1;
138
139     BBox* p_view;
140         p_view = new BBox( rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW, B_PLAIN_BORDER );
141         p_view->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
142     
143         /* Buttons */
144         /* Slow play */
145         ButtonRect.SetLeftTop(BPoint(xStart, yStart));
146         ButtonRect.SetRightBottom(ButtonRect.LeftTop() + kSkipButtonSize);
147         xStart += kRewindBitmapWidth;
148         TransportButton* p_slow = new TransportButton(ButtonRect, B_EMPTY_STRING,
149                                                                                         kSkipBackBitmapBits,
150                                                                                         kPressedSkipBackBitmapBits,
151                                                                                         kDisabledSkipBackBitmapBits,
152                                                                                         new BMessage(SLOWER_PLAY));
153         p_view->AddChild( p_slow );
154
155         /* Play Pause */
156         ButtonRect.SetLeftTop(BPoint(xStart, yStart));
157         ButtonRect.SetRightBottom(ButtonRect.LeftTop() + kPlayButtonSize);
158         xStart += kPlayPauseBitmapWidth + 1.0;
159         PlayPauseButton* p_play = new PlayPauseButton(ButtonRect, B_EMPTY_STRING,
160                                                                                         kPlayButtonBitmapBits,
161                                                                                         kPressedPlayButtonBitmapBits,
162                                                                                         kDisabledPlayButtonBitmapBits,
163                                                                                         kPlayingPlayButtonBitmapBits,
164                                                                                         kPressedPlayingPlayButtonBitmapBits,
165                                                                                         kPausedPlayButtonBitmapBits,
166                                                                                         kPressedPausedPlayButtonBitmapBits,
167                                                                                         new BMessage(START_PLAYBACK));
168    
169         p_view->AddChild( p_play );
170         p_play->SetPlaying();
171
172         /* Fast Foward */
173         ButtonRect.SetLeftTop(BPoint(xStart, yStart));
174         ButtonRect.SetRightBottom(ButtonRect.LeftTop() + kSkipButtonSize);
175         xStart += kRewindBitmapWidth;
176         TransportButton* p_fast = new TransportButton(ButtonRect, B_EMPTY_STRING,
177                                                                                         kSkipForwardBitmapBits,
178                                                                                         kPressedSkipForwardBitmapBits,
179                                                                                         kDisabledSkipForwardBitmapBits,
180                                                                                         new BMessage(FASTER_PLAY));
181         p_view->AddChild( p_fast );
182
183         /* Stop */
184         ButtonRect.SetLeftTop(BPoint(xStart, yStart));
185         ButtonRect.SetRightBottom(ButtonRect.LeftTop() + kStopButtonSize);
186         xStart += kStopBitmapWidth;
187         TransportButton* p_stop = new TransportButton(ButtonRect, B_EMPTY_STRING,
188                                                                                         kStopButtonBitmapBits,
189                                                                                         kPressedStopButtonBitmapBits,
190                                                                                         kDisabledStopButtonBitmapBits,
191                                                                                         new BMessage(STOP_PLAYBACK));
192         p_view->AddChild( p_stop );
193
194         ButtonRect.SetLeftTop(BPoint(xStart + 5, yStart + 6));
195         ButtonRect.SetRightBottom(ButtonRect.LeftTop() + kSpeakerButtonSize);
196         xStart += kSpeakerIconBitmapWidth;
197  
198         TransportButton* p_mute = new TransportButton(ButtonRect, B_EMPTY_STRING,
199                                                                                         kSpeakerIconBits,
200                                                                                         kPressedSpeakerIconBits,
201                                                                                         kSpeakerIconBits,
202                                                                                         new BMessage(VOLUME_MUTE));
203
204         p_view->AddChild( p_mute );
205  
206         /* Seek Status */       
207     rgb_color fill_color = {0,255,0};
208         p_seek = new SeekSlider(BRect(5,2,255,15), this, 0, 100,
209                                                 B_TRIANGLE_THUMB);
210         p_seek->SetValue(0);
211         p_seek->UseFillColor(true, &fill_color);
212     p_view->AddChild( p_seek );
213
214         /* Volume Slider */     
215         p_vol = new MediaSlider(BRect(xStart,20,255,30), new BMessage(VOLUME_CHG),
216                                                         0, VOLUME_MAX);
217         p_vol->SetValue(VOLUME_DEFAULT);
218         p_vol->UseFillColor(true, &fill_color);
219     p_view->AddChild( p_vol );
220     
221         /* Set size and Show */
222     AddChild( p_view );
223         ResizeTo(260,50 + menu_bar->Bounds().IntegerHeight()+1);
224     Show();
225 }
226
227 InterfaceWindow::~InterfaceWindow()
228 {
229 }
230
231 /*****************************************************************************
232  * InterfaceWindow::MessageReceived
233  *****************************************************************************/
234 void InterfaceWindow::MessageReceived( BMessage * p_message )
235 {
236         int vol_val = p_vol->Value();   // remember the current volume
237         static int playback_status;             // remember playback state
238         
239         BAlert *alert;
240         Activate();
241     switch( p_message->what )
242     {
243     case B_ABOUT_REQUESTED:
244                 alert = new BAlert(VOUT_TITLE, "BeOS " VOUT_TITLE "\n\n<www.videolan.org>", "Ok");
245             alert->Go();
246             break;      
247     
248     case OPEN_FILE:
249         if(file_panel)
250                 {
251                 file_panel->Show();
252                 break;
253                 }
254         file_panel = new BFilePanel();
255         file_panel->SetTarget(this);
256         file_panel->Show();
257         break;
258
259     case OPEN_DVD:
260             const char **device;
261             char device_method_and_name[B_FILE_NAME_LENGTH + 4];
262             if(p_message->FindString("device", device) != B_ERROR)
263                 {
264                 sprintf(device_method_and_name, "dvd:%s", *device); 
265                 intf_PlstAdd( p_main->p_playlist, PLAYLIST_END, device_method_and_name );
266                 }
267         break;
268
269     case STOP_PLAYBACK:
270         // this currently stops playback not nicely
271                 if (p_intf->p_input != NULL )
272                 {
273                         // silence the sound, otherwise very horrible
274                         if (p_main->p_aout != NULL)
275                         {
276                                 p_main->p_aout->vol = 0;
277                         }
278                         snooze(400000);
279                         input_SetStatus(p_intf->p_input, INPUT_STATUS_END);
280                 }
281         break;
282         case START_PLAYBACK:
283                 // starts playing in normal mode
284 //              if (p_intf->p_input != NULL )
285 //              {                       
286 //                      if (p_main->p_aout != NULL)
287 //                      {
288 //                              p_main->p_aout->vol = vol_val;
289 //                      }
290 //                      snooze(400000);
291 //                      input_SetStatus(p_intf->p_input, INPUT_STATUS_PLAY);
292 //                      playback_status = PLAYING;
293 //              } 
294 //              break;
295         case PAUSE_PLAYBACK:
296                 // pause the playback
297                 if (p_intf->p_input != NULL )
298                 {
299                         // mute the volume if currently playing
300                         if (playback_status == PLAYING)
301                         {
302                                 if (p_main->p_aout != NULL)
303                                 {
304                                         p_main->p_aout->vol = 0;
305                                 }
306                                 playback_status = PAUSED;
307                         }
308                         else
309                         // restore the volume
310                         {
311                                 if (p_main->p_aout != NULL)
312                                 {
313                                         p_main->p_aout->vol = vol_val;
314                                 }
315                                 playback_status = PLAYING;
316                         }
317                         snooze(400000);
318                         input_SetStatus(p_intf->p_input, INPUT_STATUS_PAUSE);
319                 }
320                 break;
321         case FASTER_PLAY:
322                 // cycle the fast playback modes
323                 if (p_intf->p_input != NULL )
324                 {
325                         if (p_main->p_aout != NULL)
326                         {
327                                 p_main->p_aout->vol = 0;
328                         }
329                         snooze(400000);
330                         input_SetStatus(p_intf->p_input, INPUT_STATUS_FASTER);
331                 }
332                 break;
333         case SLOWER_PLAY:
334                 // cycle the slow playback modes
335                 if (p_intf->p_input != NULL )
336                 {
337                         if (p_main->p_aout != NULL)
338                         {
339                                 p_main->p_aout->vol = 0;
340                         }
341                         snooze(400000);
342                         input_SetStatus(p_intf->p_input, INPUT_STATUS_SLOWER);
343                 }
344                 break;
345         case SEEK_PLAYBACK:
346                 // handled by semaphores;
347 /*          if( p_intf->p_input != NULL )
348             {
349                 float new_position;
350                     if (p_message->FindFloat("be:value", &new_position) == B_OK)
351                     {
352                         printf("%e\n", new_position);
353                         input_Seek( p_intf->p_input, new_position * 100 );
354                     }
355             } */
356                 break;
357         case VOLUME_CHG:
358                 // adjust the volume
359         if (p_main->p_aout != NULL) 
360         {
361                         p_main->p_aout->vol = vol_val;
362                 }
363                 break;
364         case VOLUME_MUTE:
365                 // mute
366         if (p_main->p_aout != NULL) 
367             {
368                         if (p_main->p_aout->vol == 0)
369                         {
370                                 p_vol->SetEnabled(true);
371                                 p_main->p_aout->vol = vol_val;
372                         }       
373                         else
374                         {
375                                 p_vol->SetEnabled(false);
376                                 p_main->p_aout->vol = 0;
377                         }
378                 }
379                 break;
380         case SELECT_CHANNEL:
381                 break;
382         case B_REFS_RECEIVED:
383     case B_SIMPLE_DATA:
384         {
385             entry_ref ref;
386             if( p_message->FindRef( "refs", &ref ) == B_OK )
387             {
388                 BPath path( &ref );
389                 char * psz_name = strdup(path.Path());
390                 intf_PlstAdd( p_main->p_playlist, PLAYLIST_END, psz_name );
391             }
392
393         }
394         break;
395     default:
396         BWindow::MessageReceived( p_message );
397         break;
398     }
399 }
400
401 /*****************************************************************************
402  * InterfaceWindow::QuitRequested
403  *****************************************************************************/
404
405 bool InterfaceWindow::QuitRequested()
406 {
407     p_intf->b_die = 1;
408
409     return( false );
410 }
411
412
413 int InterfaceWindow::GetCD(const char *directory, BMenu *cd_menu)
414
415         BDirectory dir; 
416         dir.SetTo(directory); 
417         if(dir.InitCheck() != B_NO_ERROR) { 
418                 return B_ERROR; 
419         } 
420         dir.Rewind(); 
421         BEntry entry; 
422         while(dir.GetNextEntry(&entry) >= 0) { 
423                 BPath path; 
424                 const char *name; 
425                 entry_ref e; 
426                 
427                 if(entry.GetPath(&path) != B_NO_ERROR) 
428                         continue; 
429                 name = path.Path(); 
430                 
431                 
432                 if(entry.GetRef(&e) != B_NO_ERROR) 
433                         continue; 
434
435                 if(entry.IsDirectory()) { 
436                         if(strcmp(e.name, "floppy") == 0) 
437                                 continue; // ignore floppy (it is not silent) 
438                         int devfd = GetCD(name, cd_menu);
439                         if(devfd >= 0)
440                                 {
441                                 return devfd;
442                                 }
443                 } 
444                 else { 
445                         int devfd; 
446                         device_geometry g; 
447                         status_t m;
448
449                         if(strcmp(e.name, "raw") != 0) 
450                                 continue; // ignore partitions 
451
452                         devfd = open(name, O_RDONLY); 
453                         if(devfd < 0) 
454                                 continue; 
455
456                         if(ioctl(devfd, B_GET_GEOMETRY, &g, sizeof(g)) >= 0) {
457                                 if(g.device_type == B_CD) //ensure the drive is a CD-ROM
458                                 { 
459                                         if(ioctl(devfd, B_GET_MEDIA_STATUS, &m, sizeof(m)) >= 0 )
460                                                 if(m == B_NO_ERROR) //ensure media is present
461                                                         {
462                                                         BMessage *msg;
463                                                         msg = new BMessage(OPEN_DVD);
464                                                         msg->AddString("device", name);
465                                                         BMenuItem *menu_item;
466                                                         menu_item = new BMenuItem(name, msg);
467                                                         cd_menu->AddItem(menu_item);
468                                                         continue;
469                                                         }
470                                 }
471                         }
472                         close(devfd);
473                 } 
474         }
475         return B_ERROR;
476 }
477
478
479 /*****************************************************************************
480  * MediaSlider
481  *****************************************************************************/
482 MediaSlider::MediaSlider(BRect frame,
483                                                 BMessage *message,
484                                                 int32 minValue,
485                                                 int32 maxValue)
486                                         :BSlider(frame, NULL, NULL, message, minValue, maxValue)
487 {
488
489 }
490
491 MediaSlider::~MediaSlider()
492 {
493
494 }
495
496 void MediaSlider::DrawThumb(void)
497 {
498         BRect r;
499         BView *v;
500
501         rgb_color black = {0,0,0};
502         r = ThumbFrame();
503         v = OffscreenView();
504         if(IsEnabled())
505                 v->SetHighColor(black);
506         else
507                 v->SetHighColor(tint_color(black, B_LIGHTEN_2_TINT));
508         r.InsetBy(r.IntegerWidth()/4, r.IntegerHeight()/6);
509         v->StrokeEllipse(r);
510         if(IsEnabled())
511                 v->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
512         else
513                 v->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_2_TINT));
514         r.InsetBy(1,1);
515         v->FillEllipse(r);
516 }
517
518 /*****************************************************************************
519  * SeekSlider
520  *****************************************************************************/
521 SeekSlider::SeekSlider(BRect frame,
522                                 InterfaceWindow *owner,
523                                 int32 minValue,
524                                 int32 maxValue,
525                                 thumb_style thumbType = B_TRIANGLE_THUMB)
526                         :MediaSlider(frame, NULL, minValue, maxValue)
527 {
528         fOwner = owner;
529         fMouseDown = false;
530 }
531
532 SeekSlider::~SeekSlider()
533 {
534 }
535
536 /*****************************************************************************
537  * SeekSlider::MouseDown
538  *****************************************************************************/
539 void SeekSlider::MouseDown(BPoint where)
540 {
541         BSlider::MouseDown(where);
542         fOwner->fScrubSem = create_sem(1, "Vlc::fScrubSem");
543         fMouseDown = true;                                      
544 }
545
546 /*****************************************************************************
547  * SeekSlider::MouseUp
548  *****************************************************************************/
549 void SeekSlider::MouseMoved(BPoint where, uint32 code, const BMessage *message)
550 {
551         BSlider::MouseMoved(where, code, message);
552         if (!fMouseDown)
553                 return;
554         release_sem(fOwner->fScrubSem);
555 }
556
557 /*****************************************************************************
558  * SeekSlider::MouseUp
559  *****************************************************************************/
560 void SeekSlider::MouseUp(BPoint where)
561 {
562         BSlider::MouseUp(where);
563         delete_sem(fOwner->fScrubSem);
564         fOwner->fScrubSem = B_ERROR;
565         fMouseDown = false;                                     
566 }
567         
568
569 extern "C"
570 {
571
572 /*****************************************************************************
573  * Local prototypes.
574  *****************************************************************************/
575 static int  intf_Probe     ( probedata_t *p_data );
576 static int  intf_Open      ( intf_thread_t *p_intf );
577 static void intf_Close     ( intf_thread_t *p_intf );
578 static void intf_Run       ( intf_thread_t *p_intf );
579
580 /*****************************************************************************
581  * Functions exported as capabilities. They are declared as static so that
582  * we don't pollute the namespace too much.
583  *****************************************************************************/
584 void _M( intf_getfunctions )( function_list_t * p_function_list )
585 {
586     p_function_list->pf_probe = intf_Probe;
587     p_function_list->functions.intf.pf_open  = intf_Open;
588     p_function_list->functions.intf.pf_close = intf_Close;
589     p_function_list->functions.intf.pf_run   = intf_Run;
590 }
591
592 /*****************************************************************************
593  * intf_Probe: probe the interface and return a score
594  *****************************************************************************
595  * This function tries to initialize Gnome and returns a score to the
596  * plugin manager so that it can select the best plugin.
597  *****************************************************************************/
598 static int intf_Probe( probedata_t *p_data )
599 {
600     if( TestMethod( INTF_METHOD_VAR, "beos" ) )
601     {
602         return( 999 );
603     }
604
605     return( 100 );
606 }
607
608 /*****************************************************************************
609  * intf_Open: initialize interface
610  *****************************************************************************/
611 static int intf_Open( intf_thread_t *p_intf )
612 {
613     BScreen *screen;
614     screen = new BScreen();
615     BRect rect = screen->Frame();
616     rect.top = rect.bottom-100;
617     rect.bottom -= 50;
618     rect.left += 50;
619     rect.right = rect.left + 350;
620     delete screen;
621     
622     /* Allocate instance and initialize some members */
623     p_intf->p_sys = (intf_sys_t*) malloc( sizeof( intf_sys_t ) );
624     if( p_intf->p_sys == NULL )
625     {
626         intf_ErrMsg("error: %s", strerror(ENOMEM));
627         return( 1 );
628     }
629     p_intf->p_sys->i_key = -1;
630     
631     /* Create the interface window */
632     p_intf->p_sys->p_window =
633         new InterfaceWindow( rect,
634                              VOUT_TITLE " (BeOS interface)", p_intf );
635     if( p_intf->p_sys->p_window == 0 )
636     {
637         free( p_intf->p_sys );
638         intf_ErrMsg( "error: cannot allocate memory for InterfaceWindow" );
639         return( 1 );
640     }
641     
642     return( 0 );
643 }
644
645 /*****************************************************************************
646  * intf_Close: destroy dummy interface
647  *****************************************************************************/
648 static void intf_Close( intf_thread_t *p_intf )
649 {
650     /* Destroy the interface window */
651     p_intf->p_sys->p_window->Lock();
652     p_intf->p_sys->p_window->Quit();    
653
654     /* Destroy structure */
655     free( p_intf->p_sys );
656 }
657
658
659 /*****************************************************************************
660  * intf_Run: event loop
661  *****************************************************************************/
662 static void intf_Run( intf_thread_t *p_intf )
663 {
664         
665         float progress;
666         bool seekNeeded = false;
667         
668     while( !p_intf->b_die )
669     {
670
671         /* Manage core vlc functions through the callback */
672         p_intf->pf_manage( p_intf );
673
674             /* Manage the slider */
675             if( p_intf->p_input != NULL && p_intf->p_sys->p_window != NULL)
676         {
677             if (acquire_sem(p_intf->p_sys->p_window->fScrubSem) == B_OK)
678                         {
679                                 seekNeeded = true;
680                         }               
681
682                         if (seekNeeded)
683             {
684                 uint32 seekTo = (p_intf->p_sys->p_window->p_seek->Value() * 
685                                 p_intf->p_input->stream.p_selected_area->i_size) / 100;
686                                 input_Seek( p_intf->p_input, seekTo );
687                                 seekNeeded = false;             
688             }
689                         else if (p_intf->p_sys->p_window->Lock())
690             {
691                     progress = (100. * p_intf->p_input->stream.p_selected_area->i_tell) /
692                                         p_intf->p_input->stream.p_selected_area->i_size;
693                     p_intf->p_sys->p_window->p_seek->SetValue(progress);
694                     p_intf->p_sys->p_window->Unlock();
695                 }
696          }
697
698         /* Wait a bit */
699         msleep( INTF_IDLE_SLEEP );
700     }
701 }
702
703 } /* extern "C" */