*** empty log message ***
[chuck-blob.git] / v2 / hidio_sdl.cpp
bloba918682636f87ae926466b1fed2041b2e9a0c9d3
2 /*----------------------------------------------------------------------------
3 ChucK Concurrent, On-the-fly Audio Programming Language
4 Compiler and Virtual Machine
6 Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved.
7 http://chuck.cs.princeton.edu/
8 http://soundlab.cs.princeton.edu/
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 U.S.A.
24 -----------------------------------------------------------------------------*/
26 //-----------------------------------------------------------------------------
27 // file: hidio_sdl.cpp
28 // desc: HID io over SDL
30 // author: Spencer Salazar (ssalazar@cs.princeton.edu)
31 // Ge Wang (gewang@cs.princeton.edu)
32 // Ananya Misra (amisra@cs.princeton.edu)
33 // Perry R. Cook (prc@cs.princeton.edu)
34 // date: spring 2006
35 //-----------------------------------------------------------------------------
36 #include "hidio_sdl.h"
37 #include "util_hid.h"
38 #include "chuck_errmsg.h"
40 #ifndef __PLATFORM_WIN32__
41 #include <unistd.h>
42 #else
43 #include "chuck_def.h"
44 #endif
46 #include <limits.h>
47 #include <vector>
48 #include <map>
49 using namespace std;
51 Chuck_Hid_Driver * default_drivers = NULL;
53 struct PhyHidDevIn
55 public:
56 PhyHidDevIn();
57 ~PhyHidDevIn();
58 t_CKBOOL open( t_CKINT type, t_CKUINT number );
59 t_CKBOOL read( t_CKINT element_type, t_CKINT element_num, HidMsg * msg );
60 t_CKBOOL send( const HidMsg * msg );
61 t_CKBOOL close();
62 std::string name();
64 t_CKBOOL query_element( HidMsg * query );
66 t_CKBOOL register_client( HidIn * client );
67 t_CKBOOL unregister_client( HidIn * client );
69 public:
70 CBufferAdvance * cbuf;
72 protected:
73 t_CKINT device_type;
74 t_CKUINT device_num;
76 std::vector< HidIn * > clients;
79 struct PhyHidDevOut
81 // lies
86 //-----------------------------------------------------------------------------
87 // global variables
88 //-----------------------------------------------------------------------------
89 // per-physical device buffer size
90 #define BUFFER_SIZE 8192
92 std::vector< std::vector<PhyHidDevIn *> > HidInManager::the_matrix;
93 XThread * HidInManager::the_thread = NULL;
94 t_CKBOOL HidInManager::thread_going = FALSE;
95 t_CKBOOL HidInManager::has_init = FALSE;
96 CBufferSimple * HidInManager::msg_buffer = NULL;
97 std::vector<PhyHidDevOut *> HidOutManager::the_phouts;
99 //-----------------------------------------------------------------------------
100 // name: PhyHidDevIn()
101 // desc: constructor
102 //-----------------------------------------------------------------------------
103 PhyHidDevIn::PhyHidDevIn()
105 device_type = CK_HID_DEV_NONE;
106 device_num = 0;
107 cbuf = NULL;
111 //-----------------------------------------------------------------------------
112 // name: ~PhyHidDevIn()
113 // desc: destructor
114 //-----------------------------------------------------------------------------
115 PhyHidDevIn::~PhyHidDevIn()
117 // check
118 if( device_type != CK_HID_DEV_NONE )
119 this->close();
123 //-----------------------------------------------------------------------------
124 // name: open()
125 // desc: opens the device of specified type and id
126 //-----------------------------------------------------------------------------
127 t_CKBOOL PhyHidDevIn::open( t_CKINT type, t_CKUINT number )
129 // int temp;
131 // check
132 if( device_type != CK_HID_DEV_NONE )
134 // log
135 EM_log( CK_LOG_WARNING, "PhyHidDevIn: open() failed -> already open!" );
136 return FALSE;
139 if( type <= CK_HID_DEV_NONE || type >= CK_HID_DEV_COUNT )
141 // log
142 EM_log( CK_LOG_WARNING,
143 "PhyHidDevIn: open() failed -> invalid device type %d",
144 type );
145 return FALSE;
148 if( !default_drivers[type].open )
150 EM_log( CK_LOG_WARNING,
151 "PhyHidDevIn: open() failed -> %s does not support open",
152 default_drivers[type].driver_name );
153 return FALSE;
156 if( default_drivers[type].open( ( int ) number ) )
158 EM_log( CK_LOG_WARNING,
159 "PhyHidDevIn: open() failed -> invalid %s number %d",
160 default_drivers[type].driver_name,
161 number );
162 return FALSE;
165 // allocate the buffer
166 cbuf = new CBufferAdvance;
167 if( !cbuf->initialize( BUFFER_SIZE, sizeof(HidMsg) ) )
169 // log
170 EM_log( CK_LOG_WARNING, "PhyHidDevIn: open operation failed: cannot initialize buffer" );
171 this->close();
172 return FALSE;
175 device_type = type;
176 device_num = number;
178 return TRUE;
181 //-----------------------------------------------------------------------------
182 // name: read()
183 // desc: query specific elements of the hid device
184 //-----------------------------------------------------------------------------
185 t_CKBOOL PhyHidDevIn::read( t_CKINT element_type, t_CKINT element_num, HidMsg * msg )
187 if( !default_drivers[device_type].read )
189 EM_log( CK_LOG_WARNING,
190 "PhyHidDevIn: read() failed -> %s does not support read",
191 default_drivers[device_type].driver_name );
192 return FALSE;
195 if( default_drivers[device_type].read( device_num, element_type,
196 element_num, msg ) )
198 EM_log( CK_LOG_WARNING,
199 "PhyHidDevIn: read() failed for %s %i, element type %i, element number %i",
200 default_drivers[device_type].driver_name, device_num,
201 element_type, element_num );
202 return FALSE;
205 return TRUE;
208 //-----------------------------------------------------------------------------
209 // name: send()
210 // desc: send message to a HID device
211 //-----------------------------------------------------------------------------
212 t_CKBOOL PhyHidDevIn::send( const HidMsg * msg )
214 if( !default_drivers[device_type].send )
216 EM_log( CK_LOG_WARNING,
217 "PhyHidDevIn: send() failed -> %s does not support send",
218 default_drivers[device_type].driver_name );
219 return FALSE;
222 if( default_drivers[device_type].send( device_num, msg ) )
224 EM_log( CK_LOG_WARNING,
225 "PhyHidDevIn: send() failed for %s %i",
226 default_drivers[device_type].driver_name, device_num );
227 return FALSE;
230 return TRUE;
233 //-----------------------------------------------------------------------------
234 // name: close()
235 // desc: closes the device, deallocates all associated data
236 //-----------------------------------------------------------------------------
237 t_CKBOOL PhyHidDevIn::close()
239 // check
240 if( cbuf != NULL )
242 // delete
243 SAFE_DELETE( cbuf );
244 // TODO: release references from cbuf?
247 if( device_type <= CK_HID_DEV_NONE || device_type >= CK_HID_DEV_COUNT )
249 // log
250 EM_log( CK_LOG_WARNING,
251 "PhyHidDevIn: close() failed -> invalid device type %d",
252 device_type );
253 return FALSE;
256 if( !default_drivers[device_type].close )
258 EM_log( CK_LOG_WARNING,
259 "PhyHidDevIn: close() failed -> %s does not support close",
260 default_drivers[device_type].driver_name );
261 return FALSE;
264 if( default_drivers[device_type].close( ( int ) device_num ) )
266 EM_log( CK_LOG_WARNING,
267 "PhyHidDevIn: close() failed -> invalid %s number %d",
268 default_drivers[device_type].driver_name,
269 device_num );
270 return FALSE;
273 device_type = CK_HID_DEV_NONE;
274 device_num = 0;
276 return TRUE;
279 //-----------------------------------------------------------------------------
280 // name: name()
281 // desc: retrieve device name
282 //-----------------------------------------------------------------------------
283 string PhyHidDevIn::name()
285 if( device_type == CK_HID_DEV_NONE )
286 return " ";
288 if( device_type <= CK_HID_DEV_NONE || device_type >= CK_HID_DEV_COUNT )
290 // log
291 EM_log( CK_LOG_WARNING,
292 "PhyHidDevIn: name() failed -> invalid device type %d",
293 device_type );
294 return " ";
297 if( !default_drivers[device_type].name )
298 return default_drivers[device_type].driver_name;
300 const char * _name;
302 if( !( _name = default_drivers[device_type].name( ( int ) device_num ) ) )
304 EM_log( CK_LOG_WARNING,
305 "PhyHidDevIn: name() failed -> invalid %s number %d",
306 default_drivers[device_type].driver_name,
307 device_num );
308 return " ";
311 return _name;
314 //-----------------------------------------------------------------------------
315 // name: HidOut()
316 // desc: constructor
317 //-----------------------------------------------------------------------------
318 HidOut::HidOut()
320 // phout = new PhyHidDevOut;
321 m_device_num = 0;
322 m_valid = FALSE;
323 m_suppress_output = FALSE;
329 //-----------------------------------------------------------------------------
330 // name: ~HidOut()
331 // desc: destructor
332 //-----------------------------------------------------------------------------
333 HidOut::~HidOut()
335 if( phout ) this->close();
336 SAFE_DELETE( phout );
342 //-----------------------------------------------------------------------------
343 // name: send()
344 // desc: send
345 //-----------------------------------------------------------------------------
346 t_CKUINT HidOut::send( const HidMsg * msg )
348 if( !m_valid ) return 0;
350 return 0;
356 //-----------------------------------------------------------------------------
357 // name: open
358 // desc: open HID output
359 //-----------------------------------------------------------------------------
360 t_CKBOOL HidOut::open( t_CKUINT device_num )
362 // close if already opened
363 if( m_valid )
364 this->close();
366 return m_valid = HidOutManager::open( this, (t_CKINT)device_num );
372 //-----------------------------------------------------------------------------
373 // name: close( )
374 // desc: close HID output
375 //-----------------------------------------------------------------------------
376 t_CKBOOL HidOut::close( )
378 if( !m_valid )
379 return FALSE;
381 // close
382 // HidOutManager::close( this );
384 m_valid = FALSE;
386 return TRUE;
392 //-----------------------------------------------------------------------------
393 // name: HidIn()
394 // desc: constructor
395 //-----------------------------------------------------------------------------
396 HidIn::HidIn()
398 phin = NULL;
399 m_device_num = 0;
400 m_valid = FALSE;
401 m_read_index = 0;
402 m_buffer = NULL;
403 m_suppress_output = FALSE;
404 SELF = NULL;
410 //-----------------------------------------------------------------------------
411 // name: ~HidIn()
412 // desc: destructor
413 //-----------------------------------------------------------------------------
414 HidIn::~HidIn( )
416 this->close();
417 // SAFE_DELETE( min );
423 //-----------------------------------------------------------------------------
424 // name: open()
425 // desc: open
426 //-----------------------------------------------------------------------------
427 t_CKBOOL HidIn::open( t_CKINT device_type, t_CKINT device_num )
429 // close if already opened
430 if( m_valid )
431 this->close();
433 // open
434 return m_valid = HidInManager::open( this, device_type, device_num );
440 void HidInManager::init()
442 if( has_init == FALSE )
444 // log
445 EM_log( CK_LOG_INFO, "initializing HID..." );
446 EM_pushlog();
448 init_default_drivers();
450 // allocate the matrix
451 the_matrix.resize( CK_HID_DEV_COUNT );
452 // resize each vector
453 for( vector<vector<PhyHidDevIn *> >::size_type i = 0; i < the_matrix.size(); i++ )
455 // allocate
456 the_matrix[i].resize( CK_MAX_HID_DEVICES );
459 msg_buffer = new CBufferSimple;
460 msg_buffer->initialize( 1000, sizeof( HidMsg ) );
462 #ifndef __MACOSX_CORE__
463 Hid_init();
464 #endif // __MACOSX_CORE__
466 for( size_t j = 0; j < CK_HID_DEV_COUNT; j++ )
468 if( default_drivers[j].init )
469 default_drivers[j].init();
472 #ifdef __MACOSX_CORE__
473 // start thread
474 if( the_thread == NULL )
476 // allocate
477 the_thread = new XThread;
478 // flag
479 thread_going = TRUE;
480 // start
481 the_thread->start( cb_hid_input, NULL );
483 #endif
485 has_init = TRUE;
487 EM_poplog();
491 void HidInManager::init_default_drivers()
493 default_drivers = new Chuck_Hid_Driver[CK_HID_DEV_COUNT];
495 memset( default_drivers, 0, CK_HID_DEV_COUNT * sizeof( Chuck_Hid_Driver ) );
497 default_drivers[CK_HID_DEV_JOYSTICK].init = Joystick_init;
498 default_drivers[CK_HID_DEV_JOYSTICK].quit = Joystick_quit;
499 default_drivers[CK_HID_DEV_JOYSTICK].count = Joystick_count;
500 default_drivers[CK_HID_DEV_JOYSTICK].count_elements = Joystick_count_elements;
501 default_drivers[CK_HID_DEV_JOYSTICK].open = Joystick_open;
502 default_drivers[CK_HID_DEV_JOYSTICK].close = Joystick_close;
503 default_drivers[CK_HID_DEV_JOYSTICK].name = Joystick_name;
504 default_drivers[CK_HID_DEV_JOYSTICK].driver_name = "joystick";
506 default_drivers[CK_HID_DEV_MOUSE].init = Mouse_init;
507 default_drivers[CK_HID_DEV_MOUSE].quit = Mouse_quit;
508 default_drivers[CK_HID_DEV_MOUSE].count = Mouse_count;
509 default_drivers[CK_HID_DEV_MOUSE].count_elements = Mouse_count_elements;
510 default_drivers[CK_HID_DEV_MOUSE].open = Mouse_open;
511 default_drivers[CK_HID_DEV_MOUSE].close = Mouse_close;
512 default_drivers[CK_HID_DEV_MOUSE].name = Mouse_name;
513 default_drivers[CK_HID_DEV_MOUSE].driver_name = "mouse";
515 default_drivers[CK_HID_DEV_KEYBOARD].init = Keyboard_init;
516 default_drivers[CK_HID_DEV_KEYBOARD].quit = Keyboard_quit;
517 default_drivers[CK_HID_DEV_KEYBOARD].count = Keyboard_count;
518 default_drivers[CK_HID_DEV_KEYBOARD].count_elements = Keyboard_count_elements;
519 default_drivers[CK_HID_DEV_KEYBOARD].open = Keyboard_open;
520 default_drivers[CK_HID_DEV_KEYBOARD].close = Keyboard_close;
521 default_drivers[CK_HID_DEV_KEYBOARD].send = Keyboard_send;
522 default_drivers[CK_HID_DEV_KEYBOARD].name = Keyboard_name;
523 default_drivers[CK_HID_DEV_KEYBOARD].driver_name = "keyboard";
525 default_drivers[CK_HID_DEV_WIIREMOTE].init = WiiRemote_init;
526 default_drivers[CK_HID_DEV_WIIREMOTE].quit = WiiRemote_quit;
527 default_drivers[CK_HID_DEV_WIIREMOTE].count = WiiRemote_count;
528 default_drivers[CK_HID_DEV_WIIREMOTE].open = WiiRemote_open;
529 default_drivers[CK_HID_DEV_WIIREMOTE].close = WiiRemote_close;
530 default_drivers[CK_HID_DEV_WIIREMOTE].send = WiiRemote_send;
531 default_drivers[CK_HID_DEV_WIIREMOTE].name = WiiRemote_name;
532 default_drivers[CK_HID_DEV_WIIREMOTE].driver_name = "wii remote";
534 default_drivers[CK_HID_DEV_TILTSENSOR].init = TiltSensor_init;
535 default_drivers[CK_HID_DEV_TILTSENSOR].quit = TiltSensor_quit;
536 default_drivers[CK_HID_DEV_TILTSENSOR].count = TiltSensor_count;
537 default_drivers[CK_HID_DEV_TILTSENSOR].open = TiltSensor_open;
538 default_drivers[CK_HID_DEV_TILTSENSOR].close = TiltSensor_close;
539 default_drivers[CK_HID_DEV_TILTSENSOR].read = TiltSensor_read;
540 default_drivers[CK_HID_DEV_TILTSENSOR].name = TiltSensor_name;
541 default_drivers[CK_HID_DEV_TILTSENSOR].driver_name = "tilt sensor";
544 void HidInManager::cleanup()
546 // log
547 EM_log( CK_LOG_INFO, "shutting down HID..." );
549 if( has_init )
551 // loop
552 for( vector<vector<PhyHidDevIn *> >::size_type i = 0; i < the_matrix.size(); i++ )
554 // loop
555 for( vector<PhyHidDevIn *>::size_type j = 0; j < the_matrix[i].size(); j++ )
557 // deallocate devices
558 SAFE_DELETE( the_matrix[i][j] );
562 // flag
563 thread_going = FALSE;
565 // break Hid_poll();
566 Hid_quit();
568 // clean up
569 if( the_thread != NULL )
570 SAFE_DELETE( the_thread );
572 // clean up subsystems
573 for( size_t j = 0; j < CK_HID_DEV_COUNT; j++ )
575 if( default_drivers[j].quit )
576 default_drivers[j].quit();
579 if( msg_buffer )
581 msg_buffer->cleanup();
582 SAFE_DELETE( msg_buffer );
585 // init
586 has_init = FALSE;
587 //*/
592 t_CKBOOL HidInManager::open( HidIn * hin, t_CKINT device_type, t_CKINT device_num )
594 // init?
595 if( has_init == FALSE )
597 init();
600 // check type
601 if( device_type < 1 || device_type >= CK_HID_DEV_COUNT )
603 // log
604 EM_log( CK_LOG_WARNING, "HidInManager: open() failed -> invalid type '%d'...",
605 device_type );
606 return FALSE;
609 // start thread
610 if( the_thread == NULL )
612 // allocate
613 the_thread = new XThread;
614 // flag
615 thread_going = TRUE;
616 // start
617 the_thread->start( cb_hid_input, NULL );
620 // get the vector
621 vector<PhyHidDevIn *> & v = the_matrix[device_type];
623 // see if port not already open
624 if( device_num >= (t_CKINT)v.capacity() || !v[device_num] )
626 // allocate
627 PhyHidDevIn * phin = new PhyHidDevIn;
628 // open
629 if( !phin->open( device_type, device_num ) )
631 // log
632 // should this use EM_log instead, with a higher log level?
633 EM_error2( 0, "HidIn: couldn't open %s %d...",
634 default_drivers[device_type].driver_name, device_num );
635 SAFE_DELETE( phin );
636 return FALSE;
639 // resize?
640 if( device_num >= (t_CKINT)v.capacity() )
642 t_CKINT size = v.capacity() * 2;
643 if( device_num >= size ) size = device_num + 1;
644 v.resize( size );
647 // put cbuf and rtmin in vector for future generations
648 v[device_num] = phin;
651 // set min
652 hin->phin = v[device_num];
653 // found
654 hin->m_buffer = v[device_num]->cbuf;
655 // get an index into your (you are min here) own buffer,
656 // and a free ticket to your own workshop
657 hin->m_read_index = hin->m_buffer->join( (Chuck_Event *)hin->SELF );
658 hin->m_device_num = (t_CKUINT)device_num;
660 // done
661 return TRUE;
664 //-----------------------------------------------------------------------------
665 // name: close()
666 // desc: close
667 //-----------------------------------------------------------------------------
668 t_CKBOOL HidIn::close()
670 if( !m_valid )
671 return FALSE;
673 // close
674 //HidInManager::close( this );
676 m_valid = FALSE;
678 return TRUE;
681 //-----------------------------------------------------------------------------
682 // name: empty()
683 // desc: is empty?
684 //-----------------------------------------------------------------------------
685 t_CKBOOL HidIn::empty()
687 if( !m_valid ) return TRUE;
688 return m_buffer->empty( m_read_index );
694 //-----------------------------------------------------------------------------
695 // name: recv()
696 // desc: receive message
697 //-----------------------------------------------------------------------------
698 t_CKUINT HidIn::recv( HidMsg * msg )
700 if( !m_valid ) return FALSE;
701 return m_buffer->get( msg, 1, m_read_index );
704 //-----------------------------------------------------------------------------
705 // name: read()
706 // desc: read
707 //-----------------------------------------------------------------------------
708 t_CKBOOL HidIn::read( t_CKINT type, t_CKINT num, HidMsg * msg )
710 if( !m_valid || !phin )
711 return FALSE;
713 // do read
714 return phin->read( type, num, msg );
717 //-----------------------------------------------------------------------------
718 // name: send()
719 // desc: send
720 //-----------------------------------------------------------------------------
721 t_CKBOOL HidIn::send( const HidMsg * msg )
723 if( !m_valid || !phin )
724 return FALSE;
726 // do send
727 return phin->send( msg );
730 //-----------------------------------------------------------------------------
731 // name: name()
732 // desc: get device name
733 //-----------------------------------------------------------------------------
734 std::string HidIn::name()
736 if( m_valid && phin )
737 return phin->name();
738 return " ";
741 //-----------------------------------------------------------------------------
742 // name: cb_hid_input
743 // desc: called by device implementations to push a message onto the buffer
744 //-----------------------------------------------------------------------------
745 void HidInManager::push_message( HidMsg & msg )
747 // find the queue
748 if( the_matrix[msg.device_type][msg.device_num] != NULL )
750 CBufferAdvance * cbuf = the_matrix[msg.device_type][msg.device_num]->cbuf;
751 if( cbuf != NULL )
752 // queue the thing
753 cbuf->put( &msg, 1 );
757 extern "C" void push_message( HidMsg msg )
759 HidInManager::push_message( msg );
762 //-----------------------------------------------------------------------------
763 // name: cb_hid_input
764 // desc: call back
765 //-----------------------------------------------------------------------------
766 #ifndef __PLATFORM_WIN32__
767 void * HidInManager::cb_hid_input( void * stuff )
768 #else
769 unsigned __stdcall HidInManager::cb_hid_input( void * stuff )
770 #endif
772 #ifdef __MACOSX_CORE__
773 Hid_init();
774 #endif
776 // keep going
777 while( thread_going )
779 Hid_poll();
782 // log
783 EM_log( CK_LOG_INFO, "HID thread exiting..." );
785 return 0;
788 //-----------------------------------------------------------------------------
789 // name: probeHidIn()
790 // desc: ...
791 //-----------------------------------------------------------------------------
792 void HidInManager::probeHidIn()
794 t_CKBOOL do_cleanup = !has_init;
796 init();
798 for( size_t i = 0; i < CK_HID_DEV_COUNT; i++ )
800 if( !default_drivers[i].count )
801 continue;
803 int count = default_drivers[i].count();
804 if( count == 0 )
805 continue;
807 EM_error2b( 0, "------( chuck -- %i %s device%s )------",
808 count, default_drivers[i].driver_name,
809 count > 1 ? "s" : "" );
811 for( int j = 0; j < count; j++ )
813 const char * name;
814 if( default_drivers[i].name )
815 name = default_drivers[i].name( j );
816 if( !name )
817 name = "(no name)";
819 EM_error2b( 0, " [%i] : \"%s\"", j, name );
822 EM_error2b( 0, "" );
825 if( do_cleanup )
827 cleanup();
831 //-----------------------------------------------------------------------------
832 // name: probeHidOut()
833 // desc: ...
834 //-----------------------------------------------------------------------------
835 void HidInManager::probeHidOut()
840 HidOutManager::HidOutManager()
842 the_phouts.resize( 1024 );
846 HidOutManager::~HidOutManager()
848 // yeah right
852 t_CKBOOL HidOutManager::open( HidOut * hout, t_CKINT device_num )
854 // see if port not already open
855 if( device_num >= (t_CKINT)the_phouts.capacity() || !the_phouts[device_num] )
857 // allocate
858 PhyHidDevOut * phout = new PhyHidDevOut;
860 // resize?
861 if( device_num >= (t_CKINT)the_phouts.capacity() )
863 t_CKINT size = the_phouts.capacity() * 2;
864 if( device_num >= size ) size = device_num + 1;
865 the_phouts.resize( size );
868 // put rtmout in vector for future generations
869 the_phouts[device_num] = phout;
872 // found (always) (except when it doesn't get here)
873 hout->phout = the_phouts[device_num];
874 hout->m_device_num = (t_CKUINT)device_num;
876 // done
877 return TRUE;