/* * ij.h - device-independent input jacks for VL * * this is a simple hack API. you pick a VL device. you get a list of * available input jacks as text strings. you pick one. the hack will * create a path from a properly configured video source node to the * drain node you give. hopefully someday the VL will offer this * functionality built-in so that developers do not have to deal with * this level of device-dependence. * * this hack also lets you pick the default jack and see what that was. * * WARNING this version has not been tested for all jacks * for all devices. */ #include typedef struct _IJhandle *IJhandle; /* * open a handle to use to pick input jacks. at this point you * specify a VLServer, and you specify what device you'll be using * (VL_ANY for the default device). * * if you'd just like to present a list of device names, you can use * something like this snippet: * { VLDevList devlist; vlGetDeviceList(vlServer, &devlist); for (dv=0; dv < (int)devlist.numDevices; dv++) printf("the device with VLDev %d is called [%s]\n", devlist.devices[dv].dev, devlist.devices[dv].name); } * * the VLDev (not the index dv) is what you pass to ijOpenHandle. * * during this call, ij may scan the hardware to see which flavor of a * board you have, and which options are really present. instead of * exposing the (often misleading) VL device name to the user, you can * get ij to give you a much more meaningful name (the marketing name * for the flavor of board the user has installed) with this snippet: * { VLDevList devlist; vlGetDeviceList(vlServer, &devlist); for (dv=0; dv < (int)devlist.numDevices; dv++) { VLDev dev = devlist.devices[dv].dev; VLNode drain_node = vlGetNode(vlServer, VL_DRN, VL_MEM, VL_ANY); IJhandle h = ijOpenHandle(vlServer, dev, drain_node); printf("device with VLDev %d: VL calls it [%s]" " but it's really [%s]\n", devlist.devices[dv].dev, devlist.devices[dv].name ijGetDeviceName(h)); ijCloseHandle(h); } } * * ij will use the VLServer and VLDev passed in for other functions * you call in the lifetime of this IJhandle. * * NOTE: you must also specify the handle to a drain node of the * kind you will be using (screen, memory, etc.). this VLNode is * not saved in the IJhandle and thus it will not be used by future * ij calls. ij may need to use this drain_node in order to probe * the device to find out its capabilities, and in order to translate * VL_ANY to an actual default device. In these cases, ijOpenHandle() * will actually create, set up, and destroy a path using your * drain_node. The VL leaves us no alternative to doing this. * ij uses VL_READ_ONLY, VL_READ_ONLY when setting up the path, * so in theory this should not interfere with the VL preemption * mechanism. There is one more caveat that results from this: * VL has a limitation where it cannot deal with the same VLNode * handle being used on a path on different devices. Therefore, * once you pass drain_node into ijOpenHandle(), you should never * use that node handle again. You can call vlGetNode() with the * same arguments to get a new handle that refers to the same * resources. This is shown in the code snippet above. * * NOTE: in most cases, the hardware inventory information available * to ij is derived from a scan done at BOOT TIME. Therefore, if * the user plugs in some dongle, break-out-box, or digital camera * after the machine has booted, it may not be included in ij's list * of jacks (the user must reboot to get the jacks listed). */ IJhandle ijOpenHandle(VLServer svr, VLDev device, /* VL_ANY for default device */ VLNode drain_node); /* see above. */ /* * ijCloseHandle() deallocates ij's resources for this handle: * it does not destroy any VL nodes or paths. */ void ijCloseHandle(IJhandle h); /* * returns the VLDev chosen. * useful if you specified VL_ANY to ijOpenHandle(). */ int ijGetVLDev(IJhandle h); /* * get a name suitable to describe THIS flavor of the board you have * selected. This will return the appropriate string in cases like * "Indy Video" vs. "Galileo Video". * * string belongs to ij and is valid as long as this IJhandle is alive. */ char *ijGetDeviceName(IJhandle h); /* * get number of jacks for this handle */ int ijGetNumJacks(IJhandle h); /* * get a textual description of each input jack. * * string belongs to ij and is valid as long as this IJhandle is alive. * * idx should range from 0 to ijGetNumJacks() - 1 */ char *ijGetJackName(IJhandle h, int idx); /* from 0 to NumJacks-1 */ /* * how to choose a jack: * * you pick a jack by specifying an index idx from 0 to * ijGetNumJacks()-1, or you can pick the jack currently * selected in vcp by passing VL_ANY for idx. * * use the following functions in this way to create a properly * configured path from that jack to your specified drain_node: * { ... create IJhandle h and drain node drain_node, pick idx ... int ret_idx; int node_number = ijGetNodeNumber(h, idx); VLDev device = ijGetVLDev(h); VLNode source_node = vlGetNode(svr, VL_SRC, VL_VIDEO, node_number); VLPath path = vlCreatePath(svr, device, source_node, drain_node); if (node_number < 0 || source_node < 0 || path < 0) return FALSE; if (vlSetupPaths(svr, (VLPathList)&path, 1, VL_SHARE, VL_SHARE) < 0) { vlDestroyPath(svr, path); return FALSE; } if (ijConfigurePath(h, idx, source_node, drain_node, path, node_number, &ret_idx)) { vlDestroyPath(svr, path); return FALSE; } } * * as you can see here, jack selection in the VL consists of two parts: * first you must choose the right VLNode and set up a path containing * that node, and then you must properly configure the node. in the VL, * each VL_VIDEO, VL_SRC node can serve one or more input jacks, and * the choice of video node and its required settings are totally * device-dependent. ij handles the device-dependent aspects for you. * * ijGetNodeNumber() returns a video source node number which lets * you create the video source node. * * once you have created the node and path and set up the path, * you now pass control back to ijConfigurePath() which does the * necessary vlSetControl()s to configure your path to input * data from the requested jack. * * the index of the actual jack chosen is returned in *ret_idx if * ret_idx is non-NULL. this is how you tell which is the default jack. * * ijGetNodeNumber() and ijConfigurePath() return <0 on failure. * no error return values are provided now, except for VLErrno. * * you can call this routine as many times as you like with different * arguments. calling this function does not change the state of the * IJhandle in any way. ij does not store the VLPath, VLNodes, index, * or node number it returns in the handle at all. * * VL CRAP: * * Q: why doesn't ij provide a quicky routine that just returns you an * already-configured node and path? * * A: to do this, IJ would need to create the source node, because * the video source node number varies by jack in the VL. * * then, ij would need to create the path, because we need to set * controls on the nodes we create, and VL only lets you set controls * on a node if you also specify the path the node is on! argh! * * then, ij would need to set up the paths, since the VL only lets * you set controls on a path which is set up! argh!! vlSetupPaths() * is a critical operation whose arguments and return value are of great * importance to an app; it would be difficult to do this inside ij * without removing critical control from the app. what's worse, * some apps want to pass more than one path at a time to * vlSetupPaths() so that the resource allocation is atomic. this * would not be possible if ij did the path setup. * * Q: why isn't there a function you can call to simply return * the current jack rather than creating a path? * * A: because in VL, you CAN'T get the current jack without * creating a path. argh! * * the notion of "current jack" in VL is complicated. the current jack * selected on vcp is the current jack on the current VL default source * node. ij determines the current default souce node by creating a * device path, setting it up VL_READ_ONLY, VL_READ_ONLY, reading * its node number from the control VL_DEFAULT_SOURCE, and destroying * the path. Then it gets a handle to that node, opens up a path on * that node, and determines which jack is the current jack on that node * by getting various device-dependent VL controls on that node. * it is truly unfortunate that VL users must deal with this. * */ int ijGetNodeNumber(IJhandle h, int idx); int ijConfigurePath(IJhandle h, int idx, VLNode source_node, VLNode drain_node, VLPath path, int node_number, int *ret_idx); /* * when you get a VLControlChanged event, pass in the type of the * VL control which changed, and this function will return 1 * if that control could affect the choice of input jack, or 0 * if not. see the Q and A below to see why you would want to * do this. */ int ijDoesControlAffectInputJack(IJhandle h, VLControlType type) ; /* * Some questions and answers about input jack selection: * * Q: what to do when the user chooses a different jack in vcp? * * A: when this happens, one of two things occurs: * * - if the user changes the default source node in vcp (usually * this looks like a "default in" menu), your app will receive a * VLDefaultSource event. * - if the user changes the current jack on the default source node, * your app will receive a VLControlChanged event for the relevant * controls. many menus on vcp could change the current jack on the * default source node. * * if the default source node changes, and you want to track vcp, * you MUST destroy and recreate the node and path, since the current * jack now resides on a new VLNode. * * if the jack on the default source node changes, then technically you * don't have to destroy and recreate the node and path, but you might * as well do it anyway. * * so, to track changes in vcp, * * - add VLDefaultSourceMask|VLControlChangedMask to your event mask. * - when you receive a VLDefaultSource event, destroy your * node and path, and call ijGetNodeNumber()/ijConfigurePath() * again with an argument of VL_ANY for idx. ijConfigurePath() * will return the index of the newly chosen jack. * - when you receive a VLControlChanged event, check to see whether * the control that changed could affect the current input jack, * and if so, do the same as if you had received a VLDefaultSource * event. Determining whether the VLControlChanged event could cause * an input jack change is---you guessed it---device-dependent. Use * ijDoesControlAffectInputJack() to determine this (see above). * * * Q: what if I want my app to be totally independent of vcp? * * A: sorry, bud. the system was just not designed that way. * on most SGI devices, the available input jacks are separated * into groups, and the hardware is only designed to take in data * from ONE jack per group at a time. for example, an ev1 board has * up to 4 analog video inputs, but only one of those inputs can be * used for video->memory or video->screen paths at a time. This is * true even if the paths in question are owned by totally separate * processes--the choice of a jack from the group of analog input * jacks device-global. So, when a user goes and changes the * "analog input source" setting on vcp, or any other app running on * the system changes the same control, your app WILL start getting * data from a different jack, and you cannot control or lock out this * behavior in any way. * * the "groups" of jacks are represented in the VL as separate VLNodes. * the selection of a jack within a particular VLNode is done by * setting controls on that node, usually (but not always) VL_MUXSWITCH. * on all devices so far, the VL_MUXSWITCH (or comparable) setting on a * VLNode is device-global, meaning that it affects all other VLNodes * with the same node number. Only when different jacks are present * on different VLNodes can you use them simultaneously. * * this lame behavior can be found in: vino, ev1, sirius, and impactvid. * future devices may or may not exhibit this behavior. * * given this, the best you can achieve is to be "sort of" independent * of vcp. sometimes, when people mess with vcp, it will affect your * app. sometimes, it will not. * * our video hardware and VL software have left us with only one way * of avoiding this usability nightmare, one way to present consistent * behavior: make your app input from whatever jack is selected in vcp. * if the user selects a new jack in vcp, then your app changes * immediately. this behavior would be consistently true regardless * of which jack selection control the user futzed with---your app * would not behave differently if the user changed "analog input * source" versus "default in," as many do now. The GUI user knows * nothing about VL nodes or VL_MUXSWITCH controls, and it is silly * expecting the user to understand such concepts when all they want * to do is input video from ONE jack (which is what the vast majority * want to do). If the user is doing a task where they really need * two input jacks, then sure, go ahead and expose the subtleties. * but if your app just needs to pick one input jack, and it doesn't * specifically need to pick a different jack than other apps which * are running, it may not be worthwhile for you to try and make your * app independent of vcp. you will only present a user interface * which is inconsistent with vcp and confusing. * * * Q: what to do when the user chooses a different jack in my app? * * A: destroy the node and the path yourself, and call * ijGetNodeCreatePath again. * * on the same theory of keeping your app consistent with vcp, * you would presumably want to make sure that vcp reflected * the new default input jack. as with the question above, * SOME of vcp's controls will automatically reflect your * new choice of jack, because they are device-global controls. * but not all of them. In order to keep vcp consistent, you * would have to set the default source node to the node number * of the newly selected jack. This would mean setting the * VL_DEFAULT_SOURCE parameter on the device node for the * device (XXX will VL let you do this? checking...) * */