aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2010-04-01 09:24:38 (GMT)
committerGerd Hoffmann <kraxel@redhat.com>2010-04-01 09:24:38 (GMT)
commita4a3e6b21da7d11e66364ab9ab67795a3f78020a (patch)
tree4ab98e8339361b8a4c89bedbe8888055292746b6 /common
parentf53a049ec82b6e1d0877a93387ed4d15797749a2 (diff)
v3.74
Diffstat (limited to 'common')
-rw-r--r--common/Makefile2
-rw-r--r--common/RegEdit.c1795
-rw-r--r--common/RegEdit.h65
-rw-r--r--common/RegEditI.h368
-rw-r--r--common/Subdir.mk19
-rw-r--r--common/capture.c789
-rw-r--r--common/capture.h52
-rw-r--r--common/channel.c714
-rw-r--r--common/channel.h94
-rw-r--r--common/commands.c1310
-rw-r--r--common/commands.h69
-rw-r--r--common/event.c157
-rw-r--r--common/event.h23
-rw-r--r--common/frequencies.c1250
-rw-r--r--common/frequencies.h111
-rw-r--r--common/joystick.c105
-rw-r--r--common/joystick.h2
-rw-r--r--common/lirc.c154
-rw-r--r--common/lirc.h2
-rw-r--r--common/midictrl.c311
-rw-r--r--common/midictrl.h23
-rw-r--r--common/parseconfig.c216
-rw-r--r--common/parseconfig.h7
-rw-r--r--common/sound.c68
-rw-r--r--common/sound.h6
-rw-r--r--common/webcam.c157
-rw-r--r--common/webcam.h4
27 files changed, 7873 insertions, 0 deletions
diff --git a/common/Makefile b/common/Makefile
new file mode 100644
index 0000000..4e33e30
--- /dev/null
+++ b/common/Makefile
@@ -0,0 +1,2 @@
+default:
+ cd ..; $(MAKE)
diff --git a/common/RegEdit.c b/common/RegEdit.c
new file mode 100644
index 0000000..fa01b88
--- /dev/null
+++ b/common/RegEdit.c
@@ -0,0 +1,1795 @@
+/* $XConsortium: RegEdit.c /main/5 1995/07/15 20:44:04 drk $ */
+/*
+ * @OPENGROUP_COPYRIGHT@
+ * COPYRIGHT NOTICE
+ * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc.
+ * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group
+ * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for
+ * the full copyright text.
+ *
+ * This software is subject to an open license. It may only be
+ * used on, with or for operating systems which are themselves open
+ * source systems. You must contact The Open Group for a license
+ * allowing distribution and sublicensing of this software on, with,
+ * or for operating systems which are not Open Source programs.
+ *
+ * See http://www.opengroup.org/openmotif/license for full
+ * details of the license agreement. Any use, reproduction, or
+ * distribution of the program constitutes recipient's acceptance of
+ * this agreement.
+ *
+ * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+ * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+ * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+ * OR FITNESS FOR A PARTICULAR PURPOSE
+ *
+ * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+ * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+ * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+/*
+ * HISTORY
+ */
+#include <stdio.h>
+#include <Xm/XmP.h>
+#include <X11/ShellP.h>
+#include "RegEditI.h"
+
+
+/* static forward. move from global in the original Editres code */
+static void _XEditResCheckMessages();
+static void _XEditResPutString8();
+static void _XEditResPut8();
+static void _XEditResPut16();
+static void _XEditResPut32();
+static void _XEditResPutWidgetInfo();
+static void _XEditResResetStream();
+static Boolean _XEditResGet8();
+static Boolean _XEditResGet16();
+static Boolean _XEditResGetSigned16();
+static Boolean _XEditResGet32();
+static Boolean _XEditResGetString8();
+static Boolean _XEditResGetWidgetInfo();
+
+/* the only entry point here */
+void
+XmdRegisterEditres(Widget toplevel)
+{
+ XtAddEventHandler(toplevel, (EventMask) 0, TRUE,
+ _XEditResCheckMessages, NULL);
+}
+
+
+/************************************************************
+ *
+ * Dump the content of the R5 lib/Xmu/EditresCom.c module.
+ * just move global as static.
+ *
+ ************************************************************/
+
+#define _XEditResPutBool _XEditResPut8
+#define _XEditResPutResourceType _XEditResPut8
+
+/************************************************************
+ *
+ * Local structure definitions.
+ *
+ ************************************************************/
+
+typedef enum { BlockNone, BlockSetValues, BlockAll } EditresBlock;
+
+typedef struct _SetValuesEvent {
+ EditresCommand type; /* first field must be type. */
+ WidgetInfo * widgets;
+ unsigned short num_entries; /* number of set values requests. */
+ char * name;
+ char * res_type;
+ XtPointer value;
+ unsigned short value_len;
+} SetValuesEvent;
+
+typedef struct _SVErrorInfo {
+ SetValuesEvent * event;
+ ProtocolStream * stream;
+ unsigned short * count;
+ WidgetInfo * entry;
+} SVErrorInfo;
+
+typedef struct _FindChildEvent {
+ EditresCommand type; /* first field must be type. */
+ WidgetInfo * widgets;
+ short x, y;
+} FindChildEvent;
+
+typedef struct _GenericGetEvent {
+ EditresCommand type; /* first field must be type. */
+ WidgetInfo * widgets;
+ unsigned short num_entries; /* number of set values requests. */
+} GenericGetEvent, GetResEvent, GetGeomEvent;
+
+/*
+ * Things that are common to all events.
+ */
+
+typedef struct _AnyEvent {
+ EditresCommand type; /* first field must be type. */
+ WidgetInfo * widgets;
+} AnyEvent;
+
+/*
+ * The event union.
+ */
+
+typedef union _EditresEvent {
+ AnyEvent any_event;
+ SetValuesEvent set_values_event;
+ GetResEvent get_resources_event;
+ GetGeomEvent get_geometry_event;
+ FindChildEvent find_child_event;
+} EditresEvent;
+
+typedef struct _Globals {
+ EditresBlock block;
+ SVErrorInfo error_info;
+ ProtocolStream stream;
+ ProtocolStream * command_stream; /* command stream. */
+} Globals;
+
+#define CURRENT_PROTOCOL_VERSION 4L
+
+#define streq(a,b) (strcmp( (a), (b) ) == 0)
+
+static Atom res_editor_command, res_editor_protocol, client_value;
+
+static Globals globals;
+
+static void SendFailure(), SendCommand(), InsertWidget(), ExecuteCommand();
+static void FreeEvent(), ExecuteSetValues(), ExecuteGetGeometry();
+static void ExecuteGetResources();
+
+static void GetCommand();
+static void LoadResources();
+static Boolean IsChild();
+static void DumpChildren();
+static char *DumpWidgets(), *DoSetValues(), *DoFindChild();
+static char *DoGetGeometry(), *DoGetResources();
+
+/************************************************************
+ *
+ * Resource Editor Communication Code
+ *
+ ************************************************************/
+
+/* Function Name: _XEditResCheckMessages
+ * Description: This callback routine is set on all shell widgets,
+ * and checks to see if a client message event
+ * has come from the resource editor.
+ * Arguments: w - the shell widget.
+ * data - *** UNUSED ***
+ * event - The X Event that triggered this handler.
+ * cont - *** UNUSED ***.
+ * Returns: none.
+ */
+
+/* ARGSUSED */
+static void
+_XEditResCheckMessages(w, data, event, cont)
+Widget w;
+XtPointer data;
+XEvent *event;
+Boolean *cont;
+{
+ Time time;
+ ResIdent ident;
+ static Boolean first_time = FALSE;
+ static Atom res_editor, res_comm;
+ Display * dpy;
+
+ if (event->type == ClientMessage) {
+ XClientMessageEvent * c_event = (XClientMessageEvent *) event;
+ dpy = XtDisplay(w);
+
+ if (!first_time) {
+ first_time = TRUE;
+ res_editor = XInternAtom(dpy, EDITRES_NAME, False);
+ res_editor_command = XInternAtom(dpy, EDITRES_COMMAND_ATOM, False);
+ res_editor_protocol = XInternAtom(dpy, EDITRES_PROTOCOL_ATOM,
+ False);
+
+ /* Used in later procedures. */
+ client_value = XInternAtom(dpy, EDITRES_CLIENT_VALUE, False);
+ LoadResources(w);
+ }
+
+ if ((c_event->message_type != res_editor) ||
+ (c_event->format != EDITRES_SEND_EVENT_FORMAT))
+ return;
+
+ time = c_event->data.l[0];
+ res_comm = c_event->data.l[1];
+ ident = (ResIdent) c_event->data.l[2];
+ if (c_event->data.l[3] != CURRENT_PROTOCOL_VERSION) {
+ _XEditResResetStream(&globals.stream);
+ _XEditResPut8(&globals.stream, CURRENT_PROTOCOL_VERSION);
+ SendCommand(w, res_comm, ident, ProtocolMismatch, &globals.stream);
+ return;
+ }
+
+ XtGetSelectionValue(w, res_comm, res_editor_command,
+ GetCommand, (XtPointer) (long) ident, time);
+ }
+}
+
+/* Function Name: BuildEvent
+ * Description: Takes the info out the protocol stream an constructs
+ * the proper event structure.
+ * Arguments: w - widget to own selection, in case of error.
+ * sel - selection to send error message beck in.
+ * data - the data for the request.
+ * ident - the id number we are looking for.
+ * length - length of request.
+ * Returns: the event, or NULL.
+ */
+
+#define ERROR_MESSAGE ("Client: Improperly formatted protocol request")
+
+static EditresEvent *
+BuildEvent(w, sel, data, ident, length)
+Widget w;
+Atom sel;
+XtPointer data;
+ResIdent ident;
+unsigned long length;
+{
+ EditresEvent * event;
+ ProtocolStream alloc_stream, *stream;
+ unsigned char temp;
+ register unsigned int i;
+
+ stream = &alloc_stream; /* easier to think of it this way... */
+
+ stream->current = stream->top = (unsigned char *) data;
+ stream->size = HEADER_SIZE; /* size of header. */
+
+ /*
+ * Retrieve the Header.
+ */
+
+ if (length < HEADER_SIZE) {
+ SendFailure(w, sel, ident, Failure, ERROR_MESSAGE);
+ return(NULL);
+ }
+
+ (void) _XEditResGet8(stream, &temp);
+ if (temp != ident) /* Id's don't match, ignore request. */
+ return(NULL);
+
+ event = (EditresEvent *) XtCalloc(sizeof(EditresEvent), 1);
+
+ (void) _XEditResGet8(stream, &temp);
+ event->any_event.type = (EditresCommand) temp;
+ (void) _XEditResGet32(stream, &(stream->size));
+ stream->top = stream->current; /* reset stream to top of value.*/
+
+ /*
+ * Now retrieve the data segment.
+ */
+
+ switch(event->any_event.type) {
+ case SendWidgetTree:
+ break; /* no additional info */
+ case SetValues:
+ {
+ SetValuesEvent * sv_event = (SetValuesEvent *) event;
+
+ if ( !(_XEditResGetString8(stream, &(sv_event->name)) &&
+ _XEditResGetString8(stream, &(sv_event->res_type))))
+ {
+ goto done;
+ }
+
+ /*
+ * Since we need the value length, we have to pull the
+ * value out by hand.
+ */
+
+ if (!_XEditResGet16(stream, &(sv_event->value_len)))
+ goto done;
+
+ sv_event->value = XtMalloc(sizeof(char) *
+ (sv_event->value_len + 1));
+
+ for (i = 0; i < sv_event->value_len; i++) {
+ if (!_XEditResGet8(stream,
+ (unsigned char *) sv_event->value + i))
+ {
+ goto done;
+ }
+ }
+ ((char*)sv_event->value)[i] = '\0'; /* NULL terminate that sucker. */
+
+ if (!_XEditResGet16(stream, &(sv_event->num_entries)))
+ goto done;
+
+ sv_event->widgets = (WidgetInfo *)
+ XtCalloc(sizeof(WidgetInfo), sv_event->num_entries);
+
+ for (i = 0; i < sv_event->num_entries; i++) {
+ if (!_XEditResGetWidgetInfo(stream, sv_event->widgets + i))
+ goto done;
+ }
+ }
+ break;
+ case FindChild:
+ {
+ FindChildEvent * find_event = (FindChildEvent *) event;
+
+ find_event->widgets = (WidgetInfo *)
+ XtCalloc(sizeof(WidgetInfo), 1);
+
+ if (!(_XEditResGetWidgetInfo(stream, find_event->widgets) &&
+ _XEditResGetSigned16(stream, &(find_event->x)) &&
+ _XEditResGetSigned16(stream, &(find_event->y))))
+ {
+ goto done;
+ }
+
+ }
+ break;
+ case GetGeometry:
+ case GetResources:
+ {
+ GenericGetEvent * get_event = (GenericGetEvent *) event;
+
+ if (!_XEditResGet16(stream, &(get_event->num_entries)))
+ goto done;
+
+ get_event->widgets = (WidgetInfo *)
+ XtCalloc(sizeof(WidgetInfo), get_event->num_entries);
+ for (i = 0; i < get_event->num_entries; i++) {
+ if (!_XEditResGetWidgetInfo(stream, get_event->widgets + i))
+ goto done;
+ }
+ }
+ break;
+ default:
+ {
+ char buf[BUFSIZ];
+
+ sprintf(buf, "Unknown Protocol request %d.",event->any_event.type);
+ SendFailure(w, sel, ident, buf);
+ return(NULL);
+ }
+ }
+ return(event);
+
+ done:
+
+ SendFailure(w, sel, ident, ERROR_MESSAGE);
+ FreeEvent(event);
+ return(NULL);
+}
+
+/* Function Name: FreeEvent
+ * Description: Frees the event structure and any other pieces
+ * in it that need freeing.
+ * Arguments: event - the event to free.
+ * Returns: none.
+ */
+
+static void
+FreeEvent(event)
+EditresEvent * event;
+{
+ if (event->any_event.widgets != NULL) {
+ XtFree((char *)event->any_event.widgets->ids);
+ XtFree((char *)event->any_event.widgets);
+ }
+
+ if (event->any_event.type == SetValues) {
+ XtFree(event->set_values_event.name); /* XtFree does not free if */
+ XtFree(event->set_values_event.res_type); /* value is NULL. */
+ }
+
+ XtFree((char *)event);
+}
+
+/* Function Name: GetCommand
+ * Description: Gets the Command out of the selection asserted by the
+ * resource manager.
+ * Arguments: (See Xt XtConvertSelectionProc)
+ * data - contains the ident number for the command.
+ * Returns: none.
+ */
+
+/* ARGSUSED */
+static void
+GetCommand(w, data, selection, type, value, length, format)
+Widget w;
+XtPointer data, value;
+Atom *selection, *type;
+unsigned long *length;
+int * format;
+{
+ ResIdent ident = (ResIdent) (long) data;
+ EditresEvent * event;
+
+ if ( (*type != res_editor_protocol) || (*format != EDITRES_FORMAT) )
+ return;
+
+ if ((event = BuildEvent(w, *selection, value, ident, *length)) != NULL) {
+ ExecuteCommand(w, *selection, ident, event);
+ FreeEvent(event);
+ }
+}
+
+/* Function Name: ExecuteCommand
+ * Description: Executes a command string received from the
+ * resource editor.
+ * Arguments: w - a widget.
+ * command - the command to execute.
+ * value - the associated with the command.
+ * Returns: none.
+ *
+ * NOTES: munges str
+ */
+
+/* ARGSUSED */
+static void
+ExecuteCommand(w, sel, ident, event)
+Widget w;
+Atom sel;
+ResIdent ident;
+EditresEvent * event;
+{
+ char * (*func)();
+ char * str;
+
+ if (globals.block == BlockAll) {
+ SendFailure(w, sel, ident,
+ "This client has blocked all Editres commands.");
+ return;
+ }
+ else if ((globals.block == BlockSetValues) &&
+ (event->any_event.type == SetValues)) {
+ SendFailure(w, sel, ident,
+ "This client has blocked all SetValues requests.");
+ return;
+ }
+
+ switch(event->any_event.type) {
+ case SendWidgetTree:
+ func = DumpWidgets;
+ break;
+ case SetValues:
+ func = DoSetValues;
+ break;
+ case FindChild:
+ func = DoFindChild;
+ break;
+ case GetGeometry:
+ func = DoGetGeometry;
+ break;
+ case GetResources:
+ func = DoGetResources;
+ break;
+ default:
+ {
+ char buf[BUFSIZ];
+ sprintf(buf,"Unknown Protocol request %d.",event->any_event.type);
+ SendFailure(w, sel, ident, buf);
+ return;
+ }
+ }
+
+ _XEditResResetStream(&globals.stream);
+ if ((str = (*func)(w, event, &globals.stream)) == NULL)
+ SendCommand(w, sel, ident, PartialSuccess, &globals.stream);
+ else {
+ SendFailure(w, sel, ident, str);
+ XtFree(str);
+ }
+}
+
+/* Function Name: ConvertReturnCommand
+ * Description: Converts a selection.
+ * Arguments: w - the widget that owns the selection.
+ * selection - selection to convert.
+ * target - target type for this selection.
+ * type_ret - type of the selection.
+ * value_ret - selection value;
+ * length_ret - lenght of this selection.
+ * format_ret - the format the selection is in.
+ * Returns: True if conversion was sucessful.
+ */
+
+/* ARGSUSED */
+static Boolean
+ConvertReturnCommand(w, selection, target,
+ type_ret, value_ret, length_ret, format_ret)
+Widget w;
+Atom * selection, * target, * type_ret;
+XtPointer *value_ret;
+unsigned long * length_ret;
+int * format_ret;
+{
+ /*
+ * I assume the intrinsics give me the correct selection back.
+ */
+
+ if ((*target != client_value))
+ return(FALSE);
+
+ *type_ret = res_editor_protocol;
+ *value_ret = (XtPointer) globals.command_stream->real_top;
+ *length_ret = globals.command_stream->size + HEADER_SIZE;
+ *format_ret = EDITRES_FORMAT;
+
+ return(TRUE);
+}
+
+/* Function Name: CommandDone
+ * Description: done with the selection.
+ * Arguments: *** UNUSED ***
+ * Returns: none.
+ */
+
+/* ARGSUSED */
+static void
+CommandDone(widget, selection, target)
+Widget widget;
+Atom *selection;
+Atom *target;
+{
+ /* Keep the toolkit from automaticaly freeing the selection value */
+}
+
+/* Function Name: SendFailure
+ * Description: Sends a failure message.
+ * Arguments: w - the widget to own the selection.
+ * sel - the selection to assert.
+ * ident - the identifier.
+ * str - the error message.
+ * Returns: none.
+ */
+
+static void
+SendFailure(w, sel, ident, str)
+Widget w;
+Atom sel;
+ResIdent ident;
+char * str;
+{
+ _XEditResResetStream(&globals.stream);
+ _XEditResPutString8(&globals.stream, str);
+ SendCommand(w, sel, ident, Failure, &globals.stream);
+}
+
+/* Function Name: BuildReturnPacket
+ * Description: Builds a return packet, given the data to send.
+ * Arguments: ident - the identifier.
+ * command - the command code.
+ * stream - the protocol stream.
+ * Returns: packet - the packet to send.
+ */
+
+static XtPointer
+BuildReturnPacket(ident, command, stream)
+ResIdent ident;
+EditresCommand command;
+ProtocolStream * stream;
+{
+ long old_alloc, old_size;
+ unsigned char * old_current;
+
+ /*
+ * We have cleverly keep enough space at the top of the header
+ * for the return protocol stream, so all we have to do is
+ * fill in the space.
+ */
+
+ /*
+ * Fool the insert routines into putting the header in the right
+ * place while being damn sure not to realloc (that would be very bad.
+ */
+
+ old_current = stream->current;
+ old_alloc = stream->alloc;
+ old_size = stream->size;
+
+ stream->current = stream->real_top;
+ stream->alloc = stream->size + (2 * HEADER_SIZE);
+
+ _XEditResPut8(stream, ident);
+ _XEditResPut8(stream, (unsigned char) command);
+ _XEditResPut32(stream, old_size);
+
+ stream->alloc = old_alloc;
+ stream->current = old_current;
+ stream->size = old_size;
+
+ return((XtPointer) stream->real_top);
+}
+
+/* Function Name: SendCommand
+ * Description: Builds a return command line.
+ * Arguments: w - the widget to own the selection.
+ * sel - the selection to assert.
+ * ident - the identifier.
+ * command - the command code.
+ * stream - the protocol stream.
+ * Returns: none.
+ */
+
+static void
+SendCommand(w, sel, ident, command, stream)
+Widget w;
+Atom sel;
+ResIdent ident;
+EditresCommand command;
+ProtocolStream * stream;
+{
+ BuildReturnPacket(ident, command, stream);
+ globals.command_stream = stream;
+
+/*
+ * I REALLY want to own the selection. Since this was not triggered
+ * by a user action, and I am the only one using this atom it is safe to
+ * use CurrentTime.
+ */
+
+ XtOwnSelection(w, sel, CurrentTime,
+ ConvertReturnCommand, NULL, CommandDone);
+}
+
+/************************************************************
+ *
+ * Generic Utility Functions.
+ *
+ ************************************************************/
+
+/* Function Name: FindChildren
+ * Description: Retuns all children (popup, normal and otherwise)
+ * of this widget
+ * Arguments: parent - the parent widget.
+ * children - the list of children.
+ * normal - return normal children.
+ * popup - return popup children.
+ * Returns: the number of children.
+ */
+
+static int
+FindChildren(parent, children, normal, popup)
+Widget parent, **children;
+Boolean normal, popup;
+{
+ CompositeWidget cw = (CompositeWidget) parent;
+ int i, num_children, current = 0;
+
+ num_children = 0;
+
+ if (XtIsWidget(parent) && popup)
+ num_children += parent->core.num_popups;
+
+ if (XtIsComposite(parent) && normal)
+ num_children += cw->composite.num_children;
+
+ if (num_children == 0) {
+ *children = NULL;
+ return(0);
+ }
+
+ *children =(Widget*) XtMalloc((Cardinal) sizeof(Widget) * num_children);
+
+ if (XtIsComposite(parent) && normal)
+ for (i = 0; i < cw->composite.num_children; i++,current++)
+ (*children)[current] = cw->composite.children[i];
+
+ if (XtIsWidget(parent) && popup)
+ for ( i = 0; i < parent->core.num_popups; i++, current++)
+ (*children)[current] = parent->core.popup_list[i];
+
+ return(num_children);
+}
+
+/* Function Name: IsChild
+ * Description: check to see of child is a child of parent.
+ * Arguments: top - the top of the tree.
+ * parent - the parent widget.
+ * child - the child.
+ * Returns: none.
+ */
+
+static Boolean
+IsChild(top, parent, child)
+Widget top, parent, child;
+{
+ int i, num_children;
+ Widget * children;
+
+ if (parent == NULL)
+ return(top == child);
+
+ num_children = FindChildren(parent, &children, TRUE, TRUE);
+
+ for (i = 0; i < num_children; i++) {
+ if (children[i] == child) {
+ XtFree((char *)children);
+ return(TRUE);
+ }
+ }
+
+ XtFree((char *)children);
+ return(FALSE);
+}
+
+/* Function Name: VerifyWidget
+ * Description: Makes sure all the widgets still exist.
+ * Arguments: w - any widget in the tree.
+ * info - the info about the widget to verify.
+ * Returns: an error message or NULL.
+ */
+
+static char *
+VerifyWidget(w, info)
+Widget w;
+WidgetInfo *info;
+{
+ Widget top;
+
+ register int count;
+ register Widget parent;
+ register unsigned long * child;
+
+ for (top = w; XtParent(top) != NULL; top = XtParent(top)) {}
+
+ parent = NULL;
+ child = info->ids;
+ count = 0;
+
+ while (TRUE) {
+ if (!IsChild(top, parent, (Widget) *child))
+ return(XtNewString("This widget no longer exists in the client."));
+
+ if (++count == info->num_widgets)
+ break;
+
+ parent = (Widget) *child++;
+ }
+
+ info->real_widget = (Widget) *child;
+ return(NULL);
+}
+
+/************************************************************
+ *
+ * Code to Perform SetValues operations.
+ *
+ ************************************************************/
+
+
+/* Function Name: DoSetValues
+ * Description: performs the setvalues requested.
+ * Arguments: w - a widget in the tree.
+ * event - the event that caused this action.
+ * stream - the protocol stream to add.
+ * Returns: NULL.
+ */
+
+static char *
+DoSetValues(w, event, stream)
+Widget w;
+EditresEvent * event;
+ProtocolStream * stream;
+{
+ char * str;
+ register unsigned i;
+ unsigned short count = 0;
+ SetValuesEvent * sv_event = (SetValuesEvent *) event;
+
+ _XEditResPut16(stream, count); /* insert 0, will be overwritten later. */
+
+ for (i = 0 ; i < sv_event->num_entries; i++) {
+ if ((str = VerifyWidget(w, &(sv_event->widgets[i]))) != NULL) {
+ _XEditResPutWidgetInfo(stream, &(sv_event->widgets[i]));
+ _XEditResPutString8(stream, str);
+ XtFree(str);
+ count++;
+ }
+ else
+ ExecuteSetValues(sv_event->widgets[i].real_widget,
+ sv_event, sv_event->widgets + i, stream, &count);
+ }
+
+ /*
+ * Overwrite the first 2 bytes with the real count.
+ */
+
+ *(stream->top) = count >> XER_NBBY;
+ *(stream->top + 1) = count;
+ return(NULL);
+}
+
+/* Function Name: HandleToolkitErrors
+ * Description: Handles X Toolkit Errors.
+ * Arguments: name - name of the error.
+ * type - type of the error.
+ * class - class of the error.
+ * msg - the default message.
+ * params, num_params - the extra parameters for this message.
+ * Returns: none.
+ */
+
+/* ARGSUSED */
+static void
+HandleToolkitErrors(name, type, class, msg, params, num_params)
+String name, type, class, msg, *params;
+Cardinal * num_params;
+{
+ SVErrorInfo * info = &globals.error_info;
+ char buf[BUFSIZ];
+
+ if ( streq(name, "unknownType") )
+ sprintf(buf, "The `%s' resource is not used by this widget.",
+ info->event->name);
+ else if ( streq(name, "noColormap") )
+ sprintf(buf, msg, params[0]);
+ else if (streq(name, "conversionFailed") || streq(name, "conversionError"))
+ {
+ if (streq(info->event->value, XtRString))
+ sprintf(buf,
+ "Could not convert the string '%s' for the `%s' resource.",
+ (char*)info->event->value, info->event->name);
+ else
+ sprintf(buf, "Could not convert the `%s' resource.",
+ info->event->name);
+ }
+ else
+ sprintf(buf, "Name: %s, Type: %s, Class: %s, Msg: %s",
+ name, type, class, msg);
+
+ /*
+ * Insert this info into the protocol stream, and update the count.
+ */
+
+ (*(info->count))++;
+ _XEditResPutWidgetInfo(info->stream, info->entry);
+ _XEditResPutString8(info->stream, buf);
+}
+
+/* Function Name: ExecuteSetValues
+ * Description: Performs a setvalues for a given command.
+ * Arguments: w - the widget to perform the set_values on.
+ * sv_event - the set values event.
+ * sv_info - the set_value info.
+ * Returns: none.
+ */
+
+static void
+ExecuteSetValues(w, sv_event, entry, stream, count)
+Widget w;
+SetValuesEvent * sv_event;
+WidgetInfo * entry;
+ProtocolStream * stream;
+unsigned short * count;
+{
+ XtErrorMsgHandler old;
+
+ SVErrorInfo * info = &globals.error_info;
+ info->event = sv_event; /* No data can be passed to */
+ info->stream = stream; /* an error handler, so we */
+ info->count = count; /* have to use a global, YUCK... */
+ info->entry = entry;
+
+ old = XtAppSetWarningMsgHandler(XtWidgetToApplicationContext(w),
+ HandleToolkitErrors);
+
+ XtVaSetValues(w, XtVaTypedArg,
+ sv_event->name, sv_event->res_type,
+ sv_event->value, sv_event->value_len,
+ NULL);
+
+ (void)XtAppSetWarningMsgHandler(XtWidgetToApplicationContext(w), old);
+}
+
+
+/************************************************************
+ *
+ * Code for Creating and dumping widget tree.
+ *
+ ************************************************************/
+
+/* Function Name: DumpWidgets
+ * Description: Given a widget it builds a protocol packet
+ * containing the entire widget heirarchy.
+ * Arguments: w - a widget in the tree.
+ * event - the event that caused this action.
+ * stream - the protocol stream to add.
+ * Returns: NULL
+ */
+
+/* ARGSUSED */
+static char *
+DumpWidgets(w, event, stream)
+Widget w;
+EditresEvent * event; /* UNUSED */
+ProtocolStream * stream;
+{
+ unsigned short count = 0;
+
+ /* Find Tree's root. */
+ for ( ; XtParent(w) != NULL; w = XtParent(w)) {}
+
+ /*
+ * hold space for count, overwritten later.
+ */
+
+ _XEditResPut16(stream, (unsigned int) 0);
+
+ DumpChildren(w, stream, &count);
+
+ /*
+ * Overwrite the first 2 bytes with the real count.
+ */
+
+ *(stream->top) = count >> XER_NBBY;
+ *(stream->top + 1) = count;
+ return(NULL);
+}
+
+/* Function Name: DumpChildren
+ * Description: Adds a child's name to the list.
+ * Arguments: w - the widget to dump.
+ * stream - the stream to dump to.
+ * count - number of dumps we have performed.
+ * Returns: none.
+ */
+
+/* This is a trick/kludge. To make shared libraries happier (linking
+ * against Xmu but not linking against Xt, and apparently even work
+ * as we desire on SVR4, we need to avoid an explicit data reference
+ * to applicationShellWidgetClass. XtIsTopLevelShell is known
+ * (implementation dependent assumption!) to use a bit flag. So we
+ * go that far. Then, we test whether it is an applicationShellWidget
+ * class by looking for an explicit class name. Seems pretty safe.
+ */
+static Bool isApplicationShell(w)
+ Widget w;
+{
+ register WidgetClass c;
+
+ if (!XtIsTopLevelShell(w))
+ return False;
+ for (c = XtClass(w); c; c = c->core_class.superclass) {
+ if (!strcmp(c->core_class.class_name, "ApplicationShell"))
+ return True;
+ }
+ return False;
+}
+
+static void
+DumpChildren(w, stream, count)
+Widget w;
+ProtocolStream * stream;
+unsigned short *count;
+{
+ int i, num_children;
+ Widget *children;
+ unsigned long window;
+ char * class;
+
+ (*count)++;
+
+ InsertWidget(stream, w); /* Insert the widget into the stream. */
+
+ _XEditResPutString8(stream, XtName(w)); /* Insert name */
+
+ if (isApplicationShell(w))
+ class = ((ApplicationShellWidget) w)->application.class;
+ else
+ class = XtClass(w)->core_class.class_name;
+
+ _XEditResPutString8(stream, class); /* Insert class */
+
+ if (XtIsWidget(w))
+ if (XtIsRealized(w))
+ window = XtWindow(w);
+ else
+ window = EDITRES_IS_UNREALIZED;
+ else
+ window = EDITRES_IS_OBJECT;
+
+ _XEditResPut32(stream, window); /* Insert window id. */
+
+ /*
+ * Find children and recurse.
+ */
+
+ num_children = FindChildren(w, &children, TRUE, TRUE);
+ for (i = 0; i < num_children; i++)
+ DumpChildren(children[i], stream, count);
+
+ XtFree((char *)children);
+}
+
+/************************************************************
+ *
+ * Code for getting the geometry of widgets.
+ *
+ ************************************************************/
+
+/* Function Name: DoGetGeometry
+ * Description: retrieves the Geometry of each specified widget.
+ * Arguments: w - a widget in the tree.
+ * event - the event that caused this action.
+ * stream - the protocol stream to add.
+ * Returns: NULL
+ */
+
+static char *
+DoGetGeometry(w, event, stream)
+Widget w;
+EditresEvent * event;
+ProtocolStream * stream;
+{
+ unsigned i;
+ char * str;
+ GetGeomEvent * geom_event = (GetGeomEvent *) event;
+
+ _XEditResPut16(stream, geom_event->num_entries);
+
+ for (i = 0 ; i < geom_event->num_entries; i++) {
+
+ /*
+ * Send out the widget id.
+ */
+
+ _XEditResPutWidgetInfo(stream, &(geom_event->widgets[i]));
+ if ((str = VerifyWidget(w, &(geom_event->widgets[i]))) != NULL) {
+ _XEditResPutBool(stream, True); /* an error occured. */
+ _XEditResPutString8(stream, str); /* set message. */
+ XtFree(str);
+ }
+ else
+ ExecuteGetGeometry(geom_event->widgets[i].real_widget, stream);
+ }
+ return(NULL);
+}
+
+/* Function Name: ExecuteGetGeometry
+ * Description: Gets the geometry for each widget specified.
+ * Arguments: w - the widget to get geom on.
+ * stream - stream to append to.
+ * Returns: True if no error occured.
+ */
+
+static void
+ExecuteGetGeometry(w, stream)
+Widget w;
+ProtocolStream * stream;
+{
+ int i;
+ Boolean mapped_when_man;
+ Dimension width, height, border_width;
+ Arg args[8];
+ Cardinal num_args = 0;
+ Position x, y;
+
+ if ( !XtIsRectObj(w) || (XtIsWidget(w) && !XtIsRealized(w)) ) {
+ _XEditResPutBool(stream, False); /* no error. */
+ _XEditResPutBool(stream, False); /* not visable. */
+ for (i = 0; i < 5; i++) /* fill in extra space with 0's. */
+ _XEditResPut16(stream, 0);
+ return;
+ }
+
+ XtSetArg(args[num_args], XtNwidth, &width); num_args++;
+ XtSetArg(args[num_args], XtNheight, &height); num_args++;
+ XtSetArg(args[num_args], XtNborderWidth, &border_width); num_args++;
+ XtSetArg(args[num_args], XtNmappedWhenManaged, &mapped_when_man);
+ num_args++;
+ XtGetValues(w, args, num_args);
+
+ if (!(XtIsManaged(w) && mapped_when_man) && XtIsWidget(w)) {
+ XWindowAttributes attrs;
+
+ /*
+ * The toolkit does not maintain mapping state, we have
+ * to go to the server.
+ */
+
+ if (XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attrs) != 0) {
+ if (attrs.map_state != IsViewable) {
+ _XEditResPutBool(stream, False); /* no error. */
+ _XEditResPutBool(stream, False); /* not visable. */
+ for (i = 0; i < 5; i++) /* fill in extra space with 0's. */
+ _XEditResPut16(stream, 0);
+ return;
+ }
+ }
+ else {
+ _XEditResPut8(stream, True); /* Error occured. */
+ _XEditResPutString8(stream, "XGetWindowAttributes failed.");
+ return;
+ }
+ }
+
+ XtTranslateCoords(w, -((int) border_width), -((int) border_width), &x, &y);
+
+ _XEditResPutBool(stream, False); /* no error. */
+ _XEditResPutBool(stream, True); /* Visable. */
+ _XEditResPut16(stream, x);
+ _XEditResPut16(stream, y);
+ _XEditResPut16(stream, width);
+ _XEditResPut16(stream, height);
+ _XEditResPut16(stream, border_width);
+}
+
+/************************************************************
+ *
+ * Code for executing FindChild.
+ *
+ ************************************************************/
+
+/* Function Name: PositionInChild
+ * Description: returns true if this location is in the child.
+ * Arguments: child - the child widget to check.
+ * x, y - location of point to check in the parent's
+ * coord space.
+ * Returns: TRUE if the position is in this child.
+ */
+
+static Boolean
+PositionInChild(child, x, y)
+Widget child;
+int x, y;
+{
+ Arg args[6];
+ Cardinal num;
+ Dimension width, height, border_width;
+ Position child_x, child_y;
+ Boolean mapped_when_managed;
+
+ if (!XtIsRectObj(child)) /* we must at least be a rect obj. */
+ return(FALSE);
+
+ num = 0;
+ XtSetArg(args[num], XtNmappedWhenManaged, &mapped_when_managed); num++;
+ XtSetArg(args[num], XtNwidth, &width); num++;
+ XtSetArg(args[num], XtNheight, &height); num++;
+ XtSetArg(args[num], XtNx, &child_x); num++;
+ XtSetArg(args[num], XtNy, &child_y); num++;
+ XtSetArg(args[num], XtNborderWidth, &border_width); num++;
+ XtGetValues(child, args, num);
+
+ /*
+ * The only way we will know of the widget is mapped is to see if
+ * mapped when managed is True and this is a managed child. Otherwise
+ * we will have to ask the server if this window is mapped.
+ */
+
+ if (XtIsWidget(child) && !(mapped_when_managed && XtIsManaged(child)) ) {
+ XWindowAttributes attrs;
+
+ if (XGetWindowAttributes(XtDisplay(child),
+ XtWindow(child), &attrs) != 0) {
+ /* oops */
+ }
+ else if (attrs.map_state != IsViewable)
+ return(FALSE);
+ }
+
+ return (x >= child_x) &&
+ (x <= (child_x + (Position)width + 2 * (Position)border_width)) &&
+ (y >= child_y) &&
+ (y <= (child_y + (Position)height + 2 * (Position)border_width));
+}
+
+/* Function Name: _FindChild
+ * Description: Finds the child that actually contatians the point shown.
+ * Arguments: parent - a widget that is known to contain the point
+ * specified.
+ * x, y - The point in coordinates relative to the
+ * widget specified.
+ * Returns: none.
+ */
+
+static Widget
+_FindChild(parent, x, y)
+Widget parent;
+int x, y;
+{
+ Widget * children;
+ int i = FindChildren(parent, &children, TRUE, FALSE);
+
+ while (i > 0) {
+ i--;
+
+ if (PositionInChild(children[i], x, y)) {
+ Widget child = children[i];
+
+ XtFree((char *)children);
+ return(_FindChild(child, x - child->core.x, y - child->core.y));
+ }
+ }
+
+ XtFree((char *)children);
+ return(parent);
+}
+
+/* Function Name: DoFindChild
+ * Description: finds the child that contains the location specified.
+ * Arguments: w - a widget in the tree.
+ * event - the event that caused this action.
+ * stream - the protocol stream to add.
+ * Returns: an allocated error message if something went horribly
+ * wrong and no set values were performed, else NULL.
+ */
+
+static char *
+DoFindChild(w, event, stream)
+Widget w;
+EditresEvent * event;
+ProtocolStream * stream;
+{
+ char * str;
+ Widget parent, child;
+ Position parent_x, parent_y;
+ FindChildEvent * find_event = (FindChildEvent *) event;
+
+ if ((str = VerifyWidget(w, find_event->widgets)) != NULL)
+ return(str);
+
+ parent = find_event->widgets->real_widget;
+
+ XtTranslateCoords(parent, (Position) 0, (Position) 0,
+ &parent_x, &parent_y);
+
+ child = _FindChild(parent, find_event->x - (int) parent_x,
+ find_event->y - (int) parent_y);
+
+ InsertWidget(stream, child);
+ return(NULL);
+}
+
+/************************************************************
+ *
+ * Procedures for performing GetResources.
+ *
+ ************************************************************/
+
+/* Function Name: DoGetResources
+ * Description: Gets the Resources associated with the widgets passed.
+ * Arguments: w - a widget in the tree.
+ * event - the event that caused this action.
+ * stream - the protocol stream to add.
+ * Returns: NULL
+ */
+
+static char *
+DoGetResources(w, event, stream)
+Widget w;
+EditresEvent * event;
+ProtocolStream * stream;
+{
+ unsigned int i;
+ char * str;
+ GetResEvent * res_event = (GetResEvent *) event;
+
+ _XEditResPut16(stream, res_event->num_entries); /* number of replys */
+
+ for (i = 0 ; i < res_event->num_entries; i++) {
+ /*
+ * Send out the widget id.
+ */
+ _XEditResPutWidgetInfo(stream, &(res_event->widgets[i]));
+ if ((str = VerifyWidget(w, &(res_event->widgets[i]))) != NULL) {
+ _XEditResPutBool(stream, True); /* an error occured. */
+ _XEditResPutString8(stream, str); /* set message. */
+ XtFree(str);
+ }
+ else {
+ _XEditResPutBool(stream, False); /* no error occured. */
+ ExecuteGetResources(res_event->widgets[i].real_widget,
+ stream);
+ }
+ }
+ return(NULL);
+}
+
+/* Function Name: ExecuteGetResources.
+ * Description: Gets the resources for any individual widget.
+ * Arguments: w - the widget to get resources on.
+ * stream - the protocol stream.
+ * Returns: none.
+ */
+
+static void
+ExecuteGetResources(w, stream)
+Widget w;
+ProtocolStream * stream;
+{
+ XtResourceList norm_list, cons_list;
+ Cardinal num_norm, num_cons;
+ register int i;
+
+ /*
+ * Get Normal Resources.
+ */
+
+ XtGetResourceList(XtClass(w), &norm_list, &num_norm);
+
+ if (XtParent(w) != NULL)
+ XtGetConstraintResourceList(XtClass(XtParent(w)),&cons_list,&num_cons);
+ else
+ num_cons = 0;
+
+ _XEditResPut16(stream, num_norm + num_cons); /* how many resources. */
+
+ /*
+ * Insert all the normal resources.
+ */
+
+ for ( i = 0; i < (int) num_norm; i++) {
+ _XEditResPutResourceType(stream, NormalResource);
+ _XEditResPutString8(stream, norm_list[i].resource_name);
+ _XEditResPutString8(stream, norm_list[i].resource_class);
+ _XEditResPutString8(stream, norm_list[i].resource_type);
+ }
+ XtFree((char *) norm_list);
+
+ /*
+ * Insert all the constraint resources.
+ */
+
+ if (num_cons > 0) {
+ for ( i = 0; i < (int) num_cons; i++) {
+ _XEditResPutResourceType(stream, ConstraintResource);
+ _XEditResPutString8(stream, cons_list[i].resource_name);
+ _XEditResPutString8(stream, cons_list[i].resource_class);
+ _XEditResPutString8(stream, cons_list[i].resource_type);
+ }
+ XtFree((char *) cons_list);
+ }
+}
+
+/************************************************************
+ *
+ * Code for inserting values into the protocol stream.
+ *
+ ************************************************************/
+
+/* Function Name: InsertWidget
+ * Description: Inserts the full parent heirarchy of this
+ * widget into the protocol stream as a widget list.
+ * Arguments: stream - the protocol stream.
+ * w - the widget to insert.
+ * Returns: none
+ */
+
+static void
+InsertWidget(stream, w)
+ProtocolStream * stream;
+Widget w;
+{
+ Widget temp;
+ unsigned long * widget_list;
+ register int i, num_widgets;
+
+ for (temp = w, i = 0; temp != 0; temp = XtParent(temp), i++) {}
+
+ num_widgets = i;
+ widget_list = (unsigned long *)
+ XtMalloc(sizeof(unsigned long) * num_widgets);
+
+ /*
+ * Put the widgets into the list.
+ * make sure that they are inserted in the list from parent -> child.
+ */
+
+ for (i--, temp = w; temp != NULL; temp = XtParent(temp), i--)
+ widget_list[i] = (unsigned long) temp;
+
+ _XEditResPut16(stream, num_widgets); /* insert number of widgets. */
+ for (i = 0; i < num_widgets; i++) /* insert Widgets themselves. */
+ _XEditResPut32(stream, widget_list[i]);
+
+ XtFree((char *)widget_list);
+}
+
+/************************************************************
+ *
+ * All of the following routines are public.
+ *
+ ************************************************************/
+
+/* Function Name: _XEditResPutString8
+ * Description: Inserts a string into the protocol stream.
+ * Arguments: stream - stream to insert string into.
+ * str - string to insert.
+ * Returns: none.
+ */
+
+static void
+_XEditResPutString8(stream, str)
+ProtocolStream * stream;
+char * str;
+{
+ int i, len = strlen(str);
+
+ _XEditResPut16(stream, len);
+ for (i = 0 ; i < len ; i++, str++)
+ _XEditResPut8(stream, *str);
+}
+
+/* Function Name: _XEditResPut8
+ * Description: Inserts an 8 bit integer into the protocol stream.
+ * Arguments: stream - stream to insert string into.
+ * value - value to insert.
+ * Returns: none
+ */
+
+static void
+_XEditResPut8(stream, value)
+ProtocolStream * stream;
+unsigned int value;
+{
+ unsigned char temp;
+
+ if (stream->size >= stream->alloc) {
+ stream->alloc += 100;
+ stream->real_top = (unsigned char *) XtRealloc(
+ (char *)stream->real_top,
+ stream->alloc + HEADER_SIZE);
+ stream->top = stream->real_top + HEADER_SIZE;
+ stream->current = stream->top + stream->size;
+ }
+
+ temp = (unsigned char) (value & BYTE_MASK);
+ *((stream->current)++) = temp;
+ (stream->size)++;
+}
+
+/* Function Name: _XEditResPut16
+ * Description: Inserts a 16 bit integer into the protocol stream.
+ * Arguments: stream - stream to insert string into.
+ * value - value to insert.
+ * Returns: void
+ */
+
+static void
+_XEditResPut16(stream, value)
+ProtocolStream * stream;
+unsigned int value;
+{
+ _XEditResPut8(stream, (value >> XER_NBBY) & BYTE_MASK);
+ _XEditResPut8(stream, value & BYTE_MASK);
+}
+
+/* Function Name: _XEditResPut32
+ * Description: Inserts a 32 bit integer into the protocol stream.
+ * Arguments: stream - stream to insert string into.
+ * value - value to insert.
+ * Returns: void
+ */
+
+static void
+_XEditResPut32(stream, value)
+ProtocolStream * stream;
+unsigned long value;
+{
+ int i;
+
+ for (i = 3; i >= 0; i--)
+ _XEditResPut8(stream, (value >> (XER_NBBY*i)) & BYTE_MASK);
+}
+
+/* Function Name: _XEditResPutWidgetInfo
+ * Description: Inserts the widget info into the protocol stream.
+ * Arguments: stream - stream to insert widget info into.
+ * info - info to insert.
+ * Returns: none
+ */
+
+static void
+_XEditResPutWidgetInfo(stream, info)
+ProtocolStream * stream;
+WidgetInfo * info;
+{
+ unsigned int i;
+
+ _XEditResPut16(stream, info->num_widgets);
+ for (i = 0; i < info->num_widgets; i++)
+ _XEditResPut32(stream, info->ids[i]);
+}
+
+/************************************************************
+ *
+ * Code for retrieving values from the protocol stream.
+ *
+ ************************************************************/
+
+/* Function Name: _XEditResResetStream
+ * Description: resets the protocol stream
+ * Arguments: stream - the stream to reset.
+ * Returns: none.
+ */
+
+static void
+_XEditResResetStream(stream)
+ProtocolStream * stream;
+{
+ stream->current = stream->top;
+ stream->size = 0;
+ if (stream->real_top == NULL) {
+ stream->real_top = (unsigned char *) XtRealloc(
+ (char *)stream->real_top,
+ stream->alloc + HEADER_SIZE);
+ stream->top = stream->real_top + HEADER_SIZE;
+ stream->current = stream->top + stream->size;
+ }
+}
+
+/*
+ * NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
+ *
+ * The only modified field if the "current" field.
+ *
+ * The only fields that must be set correctly are the "current", "top"
+ * and "size" fields.
+ */
+
+/* Function Name: _XEditResGetg8
+ * Description: Retrieves an unsigned 8 bit value
+ * from the protocol stream.
+ * Arguments: stream.
+ * val - a pointer to value to return.
+ * Returns: TRUE if sucessful.
+ */
+
+static Boolean
+_XEditResGet8(stream, val)
+ProtocolStream * stream;
+unsigned char * val;
+{
+ if (stream->size < (stream->current - stream->top))
+ return(FALSE);
+
+ *val = *((stream->current)++);
+ return(TRUE);
+}
+
+/* Function Name: _XEditResGet16
+ * Description: Retrieves an unsigned 16 bit value
+ * from the protocol stream.
+ * Arguments: stream.
+ * val - a pointer to value to return.
+ * Returns: TRUE if sucessful.
+ */
+
+static Boolean
+_XEditResGet16(stream, val)
+ProtocolStream * stream;
+unsigned short * val;
+{
+ unsigned char temp1, temp2;
+
+ if ( !(_XEditResGet8(stream, &temp1) && _XEditResGet8(stream, &temp2)) )
+ return(FALSE);
+
+ *val = (((unsigned short) temp1 << XER_NBBY) + ((unsigned short) temp2));
+ return(TRUE);
+}
+
+/* Function Name: _XEditResGetSigned16
+ * Description: Retrieves an signed 16 bit value from the protocol stream.
+ * Arguments: stream.
+ * val - a pointer to value to return.
+ * Returns: TRUE if sucessful.
+ */
+
+static Boolean
+_XEditResGetSigned16(stream, val)
+ProtocolStream * stream;
+short * val;
+{
+ unsigned char temp1, temp2;
+
+ if ( !(_XEditResGet8(stream, &temp1) && _XEditResGet8(stream, &temp2)) )
+ return(FALSE);
+
+ if (temp1 & (1 << (XER_NBBY - 1))) { /* If the sign bit is active. */
+ *val = -1; /* store all 1's */
+ *val &= (temp1 << XER_NBBY); /* Now and in the MSB */
+ *val &= temp2; /* and LSB */
+ }
+ else
+ *val = (((unsigned short) temp1 << XER_NBBY) + ((unsigned short) temp2));
+
+ return(TRUE);
+}
+
+/* Function Name: _XEditResGet32
+ * Description: Retrieves an unsigned 32 bit value
+ * from the protocol stream.
+ * Arguments: stream.
+ * val - a pointer to value to return.
+ * Returns: TRUE if sucessful.
+ */
+
+static Boolean
+_XEditResGet32(stream, val)
+ProtocolStream * stream;
+unsigned long * val;
+{
+ unsigned short temp1, temp2;
+
+ if ( !(_XEditResGet16(stream, &temp1) && _XEditResGet16(stream, &temp2)) )
+ return(FALSE);
+
+ *val = (((unsigned short) temp1 << (XER_NBBY * 2)) +
+ ((unsigned short) temp2));
+ return(TRUE);
+}
+
+/* Function Name: _XEditResGetString8
+ * Description: Retrieves an 8 bit string value from the protocol stream.
+ * Arguments: stream - the protocol stream
+ * str - the string to retrieve.
+ * Returns: True if retrieval was successful.
+ */
+
+static Boolean
+_XEditResGetString8(stream, str)
+ProtocolStream * stream;
+char ** str;
+{
+ unsigned short len;
+ register unsigned i;
+
+ if (!_XEditResGet16(stream, &len)) {
+ return(FALSE);
+ }
+
+ *str = XtMalloc(sizeof(char) * (len + 1));
+
+ for (i = 0; i < len; i++) {
+ if (!_XEditResGet8(stream, (unsigned char *) *str + i)) {
+ XtFree(*str);
+ *str = NULL;
+ return(FALSE);
+ }
+ }
+ (*str)[i] = '\0'; /* NULL terminate that sucker. */
+ return(TRUE);
+}
+
+/* Function Name: _XEditResGetWidgetInfo
+ * Description: Retrieves the list of widgets that follow and stores
+ * them in the widget info structure provided.
+ * Arguments: stream - the protocol stream
+ * info - the widget info struct to store into.
+ * Returns: True if retrieval was successful.
+ */
+
+static Boolean
+_XEditResGetWidgetInfo(stream, info)
+ProtocolStream * stream;
+WidgetInfo * info;
+{
+ unsigned int i;
+
+ if (!_XEditResGet16(stream, &(info->num_widgets)))
+ return(FALSE);
+
+ info->ids = (unsigned long *) XtMalloc(sizeof(long) * (info->num_widgets));
+
+ for (i = 0; i < info->num_widgets; i++) {
+ if (!_XEditResGet32(stream, info->ids + i)) {
+ XtFree((char *)info->ids);
+ info->ids = NULL;
+ return(FALSE);
+ }
+ }
+ return(TRUE);
+}
+
+/************************************************************
+ *
+ * Code for Loading the EditresBlock resource.
+ *
+ ************************************************************/
+
+/* Function Name: CvStringToBlock
+ * Description: Converts a string to an editres block value.
+ * Arguments: dpy - the display.
+ * args, num_args - **UNUSED **
+ * from_val, to_val - value to convert, and where to put result
+ * converter_data - ** UNUSED **
+ * Returns: TRUE if conversion was sucessful.
+ */
+
+/* ARGSUSED */
+static Boolean
+CvtStringToBlock(dpy, args, num_args, from_val, to_val, converter_data)
+Display * dpy;
+XrmValue * args;
+Cardinal * num_args;
+XrmValue * from_val, * to_val;
+XtPointer * converter_data;
+{
+ char ptr[BUFSIZ];
+ static EditresBlock block;
+
+/* XmuCopyISOLatin1Lowered(ptr, from_val->addr);*/
+
+
+ if (streq(ptr, "none"))
+ block = BlockNone;
+ else if (streq(ptr, "setvalues"))
+ block = BlockSetValues;
+ else if (streq(ptr, "all"))
+ block = BlockAll;
+ else {
+ Cardinal num_params = 1;
+ String params[1];
+
+ params[0] = from_val->addr;
+ XtAppWarningMsg(XtDisplayToApplicationContext(dpy),
+ "CvtStringToBlock", "unknownValue", "EditresError",
+ "Could not convert string \"%s\" to EditresBlock.",
+ params, &num_params);
+ return(FALSE);
+ }
+
+ if (to_val->addr != NULL) {
+ if (to_val->size < sizeof(EditresBlock)) {
+ to_val->size = sizeof(EditresBlock);
+ return(FALSE);
+ }
+ *(EditresBlock *)(to_val->addr) = block;
+ }
+ else
+ to_val->addr = (XtPointer) block;
+
+ to_val->size = sizeof(EditresBlock);
+ return(TRUE);
+}
+
+#define XtREditresBlock ("EditresBlock")
+
+/* Function Name: LoadResources
+ * Description: Loads a global resource the determines of this
+ * application should allow Editres requests.
+ * Arguments: w - any widget in the tree.
+ * Returns: none.
+ */
+
+static void
+LoadResources(w)
+Widget w;
+{
+ static XtResource resources[] = {
+ {"editresBlock", "EditresBlock", XtREditresBlock, sizeof(EditresBlock),
+ XtOffsetOf(Globals, block), XtRImmediate, (XtPointer) BlockNone}
+ };
+
+ for (; XtParent(w) != NULL; w = XtParent(w)) {}
+
+ XtAppSetTypeConverter(XtWidgetToApplicationContext(w),
+ XtRString, XtREditresBlock, CvtStringToBlock,
+ NULL, (Cardinal) 0, XtCacheAll, NULL);
+
+ XtGetApplicationResources( w, (caddr_t) &globals, resources,
+ XtNumber(resources), NULL, (Cardinal) 0);
+}
+
+
diff --git a/common/RegEdit.h b/common/RegEdit.h
new file mode 100644
index 0000000..5274151
--- /dev/null
+++ b/common/RegEdit.h
@@ -0,0 +1,65 @@
+/* $XConsortium: RegEdit.h /main/5 1995/07/15 20:44:09 drk $ */
+/*
+ * @OPENGROUP_COPYRIGHT@
+ * COPYRIGHT NOTICE
+ * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc.
+ * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group
+ * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for
+ * the full copyright text.
+ *
+ * This software is subject to an open license. It may only be
+ * used on, with or for operating systems which are themselves open
+ * source systems. You must contact The Open Group for a license
+ * allowing distribution and sublicensing of this software on, with,
+ * or for operating systems which are not Open Source programs.
+ *
+ * See http://www.opengroup.org/openmotif/license for full
+ * details of the license agreement. Any use, reproduction, or
+ * distribution of the program constitutes recipient's acceptance of
+ * this agreement.
+ *
+ * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+ * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+ * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+ * OR FITNESS FOR A PARTICULAR PURPOSE
+ *
+ * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+ * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+ * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+/*
+ * HISTORY
+ */
+
+ /* Ensure that the file be included only once. */
+#ifndef _XmdRegEdit_h
+#define _XmdRegEdit_h
+
+#include <Xm/Xm.h>
+
+/* Allow for C++ compilation. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern void XmdRegisterEditres(Widget toplevel);
+
+/* Allow for C++ compilation. */
+#ifdef __cplusplus
+} /* Close scope of 'extern "C"' declaration which encloses file. */
+#endif
+
+
+/* Ensure that the file be included only once. */
+#endif /* _XmdRegEdit_h */
+/* DON'T ADD ANYTHING AFTER THIS #endif */
+
diff --git a/common/RegEditI.h b/common/RegEditI.h
new file mode 100644
index 0000000..4608593
--- /dev/null
+++ b/common/RegEditI.h
@@ -0,0 +1,368 @@
+/*
+ * @OPENGROUP_COPYRIGHT@
+ * COPYRIGHT NOTICE
+ * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc.
+ * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group
+ * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for
+ * the full copyright text.
+ *
+ * This software is subject to an open license. It may only be
+ * used on, with or for operating systems which are themselves open
+ * source systems. You must contact The Open Group for a license
+ * allowing distribution and sublicensing of this software on, with,
+ * or for operating systems which are not Open Source programs.
+ *
+ * See http://www.opengroup.org/openmotif/license for full
+ * details of the license agreement. Any use, reproduction, or
+ * distribution of the program constitutes recipient's acceptance of
+ * this agreement.
+ *
+ * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+ * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+ * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+ * OR FITNESS FOR A PARTICULAR PURPOSE
+ *
+ * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+ * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+ * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+/*
+ * HISTORY
+ */
+
+
+/* $XConsortium: RegEditI.h /main/4 1995/07/14 10:05:01 drk $ */
+
+/*
+
+Copyright (c) 1989 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+/*
+ * Author: Chris D. Peterson, MIT X Consortium
+ */
+
+/************************************************************
+
+ The Editres Protocol
+
+
+ The Client message sent to the application is:
+
+ ATOM = "ResEditor" --- RES_EDITOR_NAME
+
+ FORMAT = 32 --- RES_EDIT_SEND_EVENT_FORMAT
+
+ l[0] = timestamp
+ l[1] = command atom name
+ l[2] = ident of command.
+ l[3] = protocol version number to use.
+
+
+
+ The binary protocol has the following format:
+
+ Card8: 8-bit unsingned integer
+ Card16: 16-bit unsingned integer
+ Card32: 32-bit unsingned integer
+ Int16: 16-bit signed integer
+ Window: 32-bit value
+ Widget: 32-bit value
+ String8: ListOfCard8
+
+ [a][b][c] represent an exclusive list of choices.
+
+ All widgets are passed as a list of widgets, containing the
+ full instance heirarch of this widget. The hierarchy is ordered
+ from parent to child. Thus the first element of each list is
+ the root of the widget tree (this makes verifying that the widget
+ still exists, MUCH faster).
+
+ ListOfFoo comprises a list of things in the following format:
+
+ number: Card16
+ <number> things: ????
+
+ This is a synchronous protocol, every request MUST be followed by a
+ reply.
+
+ Request:
+
+ Serial Number: Card8
+ Op Code: Card8 - { SendWidgetTree = 0,
+ SetValues = 1,
+ GetResources = 2,
+ GetGeometry = 3,
+ FindChild = 4,
+ GetValues = 5 }
+ Length: Card32
+ Data:
+
+ Reply:
+
+ Serial Number: Card8
+ Type: Card8 - { Formatted = 0,
+ Unformatted = 1,
+ ProtocolMismatch = 2
+ }
+ Length: Card32
+
+
+ Byte Order:
+
+ All Fields are MSB -> LSB
+
+ Data:
+
+ Formatted:
+
+ The data contains the reply information for the request as
+ specified below if the reply type is "Formatted". The return
+ values for the other reply types are shown below.
+
+ Unformatted:
+
+ Message: String8
+
+ ProtocolMismatch:
+
+ RequestedVersion: Card8
+
+------------------------------------------------------------
+
+ SendWidgetTree:
+
+ --->
+
+ Number of Entries: Card16
+ Entry:
+ widget: ListOfWidgets
+ name: String8
+ class: String8
+ window: Card32
+ toolkit: String8
+
+ Send Widget Tree returns the toolkit type, and a fuly specified list
+ of widgets for each widget in the tree. This is enough information
+ to completely reconstruct the entire widget heirarchy.
+
+ The window return value contains the Xid of the window currently
+ used by this widget. If the widget is unrealized then 0 is returned,
+ and if widget is a non-windowed object a value of 2 is returned.
+
+ SetValues:
+
+ name: String8
+ type: String8
+ value: String8
+ Number of Entries: Card16
+ Entry:
+ widget: ListOfWidgets
+
+ --->
+
+ Number of Entries: Card16
+ Entry:
+ widget: ListOfWidgets
+ message: String8
+
+ SetValues will allow the same resource to be set on a number of
+ widgets. This function will return an error message if the SetValues
+ request caused an Xt error.
+
+ GetValues:
+
+ names: ListOfString8
+ widget: Widget
+
+ --->
+ novalues: ListOfCard16
+ values: ListOfString8
+
+ GetValues will allow a number of resource values to be read
+ on a particular widget. The request specifies the names of
+ the resources wanted and the widget id these resources are
+ from. The reply returns a list of indices from the requests
+ name list of resources for which a value can not be returned.
+ It also returns a list of returned values, in the order of the
+ requests names list, skipping those indices present in novalues.
+
+ GetResources:
+
+ Number of Entries: Card16
+ Entry
+ widget: ListOfWidgets:
+
+ ---->
+
+ Number of Entries: Card16
+ Entry
+ Widget: ListOfWidgets:
+ Error: Bool
+
+ [ Message: String 8 ]
+ [ Number of Resources: Card16
+ Resource:
+ Kind: {normal, constraint}
+ Name: String8
+ Class: String8
+ Type: String8 ]
+
+ GetResource retrieves the kind, name, class and type for every
+ widget passed to it. If an error occured with the resource fetch
+ Error will be set to True for the given widget and a message
+ is returned rather than the resource info.
+
+ GetGeometry:
+
+ Number of Entries: Card16
+ Entry
+ Widget: ListOfWidgets:
+
+ ---->
+
+ Number of Entries: Card16
+ Entry
+ Widget: ListOfWidgets:
+ Error: Bool
+
+ [ message: String 8 ]
+ [ mapped: Boolean
+ X: Int16
+ Y: Int16
+ Width: Card16
+ Height: Card16
+ BorderWidth: Card16 ]
+
+ GetGeometry retreives the mapping state, x, y, width, height
+ and border width for each widget specified. If an error occured
+ with the geometry fetch "Error" will be set to True for the given
+ widget and a message is returned rather than the geometry info.
+ X an Y corrospond to the root coordinates of the upper left corner
+ of the widget (outside the window border).
+
+ FindChild:
+
+ Widget: ListOfWidgets
+ X: Int16
+ Y: Int16
+
+ --->
+
+ Widget: ListOfWidgets
+
+ Find Child returns a descendent of the widget specified that
+ is at the root coordinates specified.
+
+ NOTE:
+
+ The returned widget is undefined if the point is contained in
+ two or more mapped widgets, or in two overlapping Rect objs.
+
+ GetValues:
+
+ names: ListOfString8
+ widget: Widget
+
+ --->
+
+ values: ListOfString8
+
+ GetValues will allow a number of resource values to be read
+ on a particular widget. Currently only InterViews 3.0.1 Styles
+ and their attributes are supported. In addition, the current
+ user interface only supports the return of 1 resource. The ability
+ to specify and return multiple resources is defined for future editres
+ interfaces where some or all of a widgets resource values are returned
+ and displayed at once.
+
+
+************************************************************/
+
+#include <X11/Intrinsic.h>
+#include <X11/Xfuncproto.h>
+
+#define XER_NBBY 8 /* number of bits in a byte */
+#define BYTE_MASK 255
+
+#define HEADER_SIZE 6
+
+#define EDITRES_IS_OBJECT 2
+#define EDITRES_IS_UNREALIZED 0
+
+/*
+ * Format for atoms.
+ */
+
+#define EDITRES_FORMAT 8
+#define EDITRES_SEND_EVENT_FORMAT 32
+
+/*
+ * Atoms
+ */
+
+#define EDITRES_NAME "Editres"
+#define EDITRES_COMMAND_ATOM "EditresCommand"
+#define EDITRES_COMM_ATOM "EditresComm"
+#define EDITRES_CLIENT_VALUE "EditresClientVal"
+#define EDITRES_PROTOCOL_ATOM "EditresProtocol"
+
+typedef enum { SendWidgetTree = 0,
+ SetValues = 1,
+ GetResources = 2,
+ GetGeometry = 3,
+ FindChild = 4,
+ GetValues = 5
+ } EditresCommand;
+
+typedef enum {NormalResource = 0, ConstraintResource = 1} ResourceType;
+
+/*
+ * The type of a resource identifier.
+ */
+
+typedef unsigned char ResIdent;
+
+typedef enum {PartialSuccess= 0, Failure= 1, ProtocolMismatch= 2} EditResError;
+
+typedef struct _WidgetInfo {
+ unsigned short num_widgets;
+ unsigned long * ids;
+ Widget real_widget;
+} WidgetInfo;
+
+typedef struct _ProtocolStream {
+ unsigned long size, alloc;
+ unsigned char *real_top, *top, *current;
+} ProtocolStream;
+
diff --git a/common/Subdir.mk b/common/Subdir.mk
new file mode 100644
index 0000000..58aba3d
--- /dev/null
+++ b/common/Subdir.mk
@@ -0,0 +1,19 @@
+
+OBJS-common-capture := \
+ common/sound.o \
+ common/webcam.o \
+ common/frequencies.o \
+ common/commands.o \
+ common/parseconfig.o \
+ common/capture.o \
+ common/event.o \
+ libng/libng.a
+
+OBJS-common-input := \
+ common/lirc.o \
+ common/joystick.o \
+ common/midictrl.o
+
+common/channel-no-x11.o:: common/channel.c
+ $(CC) $(CFLAGS) -DNO_X11=1 -Wp,-MD,$*.dep -c -o $@ $<
+ @sed -e "s|.*\.o:|$@::|" < $*.dep > $*.d && rm -f $*.dep
diff --git a/common/capture.c b/common/capture.c
new file mode 100644
index 0000000..a309bf2
--- /dev/null
+++ b/common/capture.c
@@ -0,0 +1,789 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include "grab-ng.h"
+#include "commands.h" /* FIXME: *drv globals */
+#include "sound.h"
+#include "capture.h"
+#include "webcam.h"
+
+#define MAX_THREADS 4
+#define REORDER_SIZE 8
+
+/*-------------------------------------------------------------------------*/
+/* data fifos (audio/video) */
+
+void
+fifo_init(struct FIFO *fifo, char *name, int slots, int writers)
+{
+ pthread_mutex_init(&fifo->lock, NULL);
+ pthread_cond_init(&fifo->hasdata, NULL);
+ fifo->name = name;
+ fifo->slots = slots;
+ fifo->writers = writers;
+ fifo->read = 0;
+ fifo->write = 0;
+ fifo->eof = 0;
+ fifo->max = 0;
+}
+
+int
+fifo_put(struct FIFO *fifo, void *data)
+{
+ int full;
+
+ pthread_mutex_lock(&fifo->lock);
+ if (NULL == data) {
+ fifo->eof++;
+ if (debug)
+ fprintf(stderr,"fifo %s: EOF %d/%d\n",
+ fifo->name,fifo->eof,fifo->writers);
+ if (fifo->writers == fifo->eof)
+ pthread_cond_broadcast(&fifo->hasdata);
+ pthread_mutex_unlock(&fifo->lock);
+ return 0;
+ }
+ if ((fifo->write + 1) % fifo->slots == fifo->read) {
+ pthread_mutex_unlock(&fifo->lock);
+ fprintf(stderr,"fifo %s is full\n",fifo->name);
+ return -1;
+ }
+ if (debug > 1)
+ fprintf(stderr,"put %s %d=%p [pid=%d]\n",
+ fifo->name,fifo->write,data,getpid());
+ fifo->data[fifo->write] = data;
+ fifo->write++;
+ full = (fifo->write + fifo->slots - fifo->read) % fifo->slots;
+ if (fifo->max < full)
+ fifo->max = full;
+ if (fifo->write >= fifo->slots)
+ fifo->write = 0;
+ pthread_cond_signal(&fifo->hasdata);
+ pthread_mutex_unlock(&fifo->lock);
+ return 0;
+}
+
+void*
+fifo_get(struct FIFO *fifo)
+{
+ void *data;
+
+ pthread_mutex_lock(&fifo->lock);
+ while (fifo->write == fifo->read && fifo->writers != fifo->eof) {
+ pthread_cond_wait(&fifo->hasdata, &fifo->lock);
+ }
+ if (fifo->write == fifo->read) {
+ pthread_cond_signal(&fifo->hasdata);
+ pthread_mutex_unlock(&fifo->lock);
+ return NULL;
+ }
+ if (debug > 1)
+ fprintf(stderr,"get %s %d=%p [pid=%d]\n",
+ fifo->name,fifo->read,fifo->data[fifo->read],getpid());
+ data = fifo->data[fifo->read];
+ fifo->read++;
+ if (fifo->read >= fifo->slots)
+ fifo->read = 0;
+ pthread_mutex_unlock(&fifo->lock);
+ return data;
+}
+
+static void*
+flushit(void *arg)
+{
+ int old;
+
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,&old);
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&old);
+ for (;;) {
+ sleep(1);
+ sync();
+ }
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+/* color space conversion / compression functions + thread */
+
+struct ng_convert_handle {
+ /* converter data / state */
+ struct ng_video_fmt ifmt;
+ struct ng_video_fmt ofmt;
+ int isize;
+ int osize;
+ struct ng_video_conv *conv;
+ void *chandle;
+
+ /* thread data */
+ struct FIFO *in;
+ struct FIFO *out;
+};
+
+struct ng_convert_handle*
+ng_convert_alloc(struct ng_video_conv *conv,
+ struct ng_video_fmt *i,
+ struct ng_video_fmt *o)
+{
+ struct ng_convert_handle *h;
+
+ h = malloc(sizeof(*h));
+ if (NULL == h)
+ return 0;
+ memset(h,0,sizeof(*h));
+
+ /* fixup output image size to match incoming */
+ o->width = i->width;
+ o->height = i->height;
+ if (0 == o->bytesperline)
+ o->bytesperline = o->width * ng_vfmt_to_depth[o->fmtid] / 8;
+
+ h->ifmt = *i;
+ h->ofmt = *o;
+ if (conv)
+ h->conv = conv;
+ return h;
+}
+
+void
+ng_convert_init(struct ng_convert_handle *h)
+{
+ if (0 == h->ifmt.bytesperline)
+ h->ifmt.bytesperline = h->ifmt.width *
+ ng_vfmt_to_depth[h->ifmt.fmtid] / 8;
+ if (0 == h->ofmt.bytesperline)
+ h->ofmt.bytesperline = h->ofmt.width *
+ ng_vfmt_to_depth[h->ofmt.fmtid] / 8;
+
+ h->isize = h->ifmt.height * h->ifmt.bytesperline;
+ if (0 == h->isize)
+ h->isize = h->ifmt.width * h->ifmt.height * 3;
+ h->osize = h->ofmt.height * h->ofmt.bytesperline;
+ if (0 == h->osize)
+ h->osize = h->ofmt.width * h->ofmt.height * 3;
+
+ if (h->conv)
+ h->chandle = h->conv->init(&h->ofmt,h->conv->priv);
+
+ if (debug) {
+ fprintf(stderr,"convert-in : %dx%d %s (size=%d)\n",
+ h->ifmt.width, h->ifmt.height,
+ ng_vfmt_to_desc[h->ifmt.fmtid], h->isize);
+ fprintf(stderr,"convert-out: %dx%d %s (size=%d)\n",
+ h->ofmt.width, h->ofmt.height,
+ ng_vfmt_to_desc[h->ofmt.fmtid], h->osize);
+ }
+}
+
+static void
+ng_convert_copyframe(struct ng_video_buf *dest,
+ struct ng_video_buf *src)
+{
+ int i,sw,dw;
+ unsigned char *sp,*dp;
+
+ dw = dest->fmt.width * ng_vfmt_to_depth[dest->fmt.fmtid] / 8;
+ sw = src->fmt.width * ng_vfmt_to_depth[src->fmt.fmtid] / 8;
+ if (src->fmt.bytesperline == sw && dest->fmt.bytesperline == dw) {
+ /* can copy in one go */
+ memcpy(dest->data, src->data,
+ src->fmt.bytesperline * src->fmt.height);
+ } else {
+ /* copy line by line */
+ dp = dest->data;
+ sp = src->data;
+ for (i = 0; i < src->fmt.height; i++) {
+ memcpy(dp,sp,dw);
+ dp += dest->fmt.bytesperline;
+ sp += src->fmt.bytesperline;
+ }
+ }
+}
+
+struct ng_video_buf*
+ng_convert_frame(struct ng_convert_handle *h,
+ struct ng_video_buf *dest,
+ struct ng_video_buf *buf)
+{
+ if (NULL == buf)
+ return NULL;
+
+ if (NULL == dest && NULL != h->conv)
+ dest = ng_malloc_video_buf(&h->ofmt,h->osize);
+
+ if (NULL != dest) {
+ dest->fmt = h->ofmt;
+ dest->size = h->osize;
+ if (NULL != h->conv) {
+ h->conv->frame(h->chandle,dest,buf);
+ } else {
+ ng_convert_copyframe(dest,buf);
+ }
+ dest->info = buf->info;
+ ng_release_video_buf(buf);
+ buf = dest;
+ }
+
+#if 1 /* FIXME */
+ if (NULL != webcam && 0 == webcam_put(webcam,buf)) {
+ free(webcam);
+ webcam = NULL;
+ }
+#endif
+ return buf;
+}
+
+void
+ng_convert_fini(struct ng_convert_handle *h)
+{
+ if (h->conv)
+ h->conv->fini(h->chandle);
+ free(h);
+}
+
+struct ng_video_buf*
+ng_convert_single(struct ng_convert_handle *h, struct ng_video_buf *in)
+{
+ struct ng_video_buf *out;
+
+ ng_convert_init(h);
+ out = ng_convert_frame(h,NULL,in);
+ ng_convert_fini(h);
+ return out;
+}
+
+void*
+ng_convert_thread(void *arg)
+{
+ struct ng_convert_handle *h = arg;
+ struct ng_video_buf *in, *out;
+
+ if (debug)
+ fprintf(stderr,"convert_thread start [pid=%d]\n",getpid());
+ ng_convert_init(h);
+ for (;;) {
+ in = fifo_get(h->in);
+ if (NULL == in)
+ break;
+ out = ng_convert_frame(h,NULL,in);
+ fifo_put(h->out,out);
+ }
+ fifo_put(h->out,NULL);
+ ng_convert_fini(h);
+ if (debug)
+ fprintf(stderr,"convert_thread done [pid=%d]\n",getpid());
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+/* parameter negotiation -- look what the driver can do and what */
+/* convert functions are available */
+
+int
+ng_grabber_setformat(struct ng_video_fmt *fmt, int fix_ratio)
+{
+ struct ng_video_fmt gfmt;
+ int rc;
+
+ /* no capture support */
+ if (!(f_drv & CAN_CAPTURE))
+ return -1;
+
+ /* try setting the format */
+ gfmt = *fmt;
+ rc = drv->setformat(h_drv,&gfmt);
+ if (debug)
+ fprintf(stderr,"setformat: %s (%dx%d): %s\n",
+ ng_vfmt_to_desc[gfmt.fmtid],
+ gfmt.width,gfmt.height,
+ (0 == rc) ? "ok" : "failed");
+ if (0 != rc)
+ return -1;
+
+ if (fix_ratio) {
+ /* fixup aspect ratio if needed */
+ ng_ratio_fixup(&gfmt.width, &gfmt.height, NULL, NULL);
+ gfmt.bytesperline = 0;
+ if (0 != drv->setformat(h_drv,&gfmt)) {
+ fprintf(stderr,"Oops: ratio size renegotiation failed\n");
+ exit(1);
+ }
+ }
+
+ /* return the real format the grabber uses now */
+ *fmt = gfmt;
+ return 0;
+}
+
+struct ng_video_conv*
+ng_grabber_findconv(struct ng_video_fmt *fmt,
+ int fix_ratio)
+{
+ struct ng_video_fmt gfmt;
+ struct ng_video_conv *conv;
+ int i;
+
+ /* check all available conversion functions */
+ for (i = 0;;) {
+ conv = ng_conv_find(fmt->fmtid, &i);
+ if (NULL == conv)
+ break;
+ gfmt = *fmt;
+ gfmt.fmtid = conv->fmtid_in;
+ if (0 == ng_grabber_setformat(&gfmt,fix_ratio))
+ goto found;
+ }
+ fprintf(stderr,"no way to get: %dx%d %s\n",
+ fmt->width,fmt->height,ng_vfmt_to_desc[fmt->fmtid]);
+ return NULL;
+
+ found:
+ *fmt = gfmt;
+ return conv;
+}
+
+struct ng_video_buf*
+ng_grabber_grab_image(int single)
+{
+ return single ? drv->getimage(h_drv) : drv->nextframe(h_drv);
+}
+
+struct ng_video_buf*
+ng_grabber_get_image(struct ng_video_fmt *fmt)
+{
+ struct ng_video_fmt gfmt;
+ struct ng_video_conv *conv;
+ struct ng_convert_handle *ch;
+ struct ng_video_buf *buf;
+
+ if (0 == ng_grabber_setformat(fmt,1))
+ return ng_grabber_grab_image(1);
+ gfmt = *fmt;
+ if (NULL == (conv = ng_grabber_findconv(&gfmt,1)))
+ return NULL;
+ ch = ng_convert_alloc(conv,&gfmt,fmt);
+ if (NULL == (buf = ng_grabber_grab_image(1)))
+ return NULL;
+ buf = ng_convert_single(ch,buf);
+ return buf;
+}
+
+/*-------------------------------------------------------------------------*/
+
+struct movie_handle {
+ /* general */
+ pthread_mutex_t lock;
+ const struct ng_writer *writer;
+ void *handle;
+ pthread_t tflush;
+ long long start;
+ long long rts;
+ long long stopby;
+ int slots;
+
+ /* video */
+ struct ng_video_fmt vfmt;
+ int fps;
+ int frames;
+ int seq;
+ struct FIFO vfifo;
+ pthread_t tvideo;
+ long long vts;
+
+ /* video converter thread */
+ struct FIFO cfifo;
+ int cthreads;
+ struct ng_convert_handle *hconv[MAX_THREADS];
+ pthread_t tconv[MAX_THREADS];
+
+ /* audio */
+ const struct ng_dsp_driver *dsp;
+ void *hdsp;
+ struct ng_audio_fmt afmt;
+ unsigned long bytes_per_sec;
+ unsigned long bytes;
+ struct FIFO afifo;
+ pthread_t taudio;
+ pthread_t raudio;
+ long long ats;
+};
+
+static void*
+writer_audio_thread(void *arg)
+{
+ struct movie_handle *h = arg;
+ struct ng_audio_buf *buf;
+
+ if (debug)
+ fprintf(stderr,"writer_audio_thread start [pid=%d]\n",getpid());
+ for (;;) {
+ buf = fifo_get(&h->afifo);
+ if (NULL == buf)
+ break;
+ pthread_mutex_lock(&h->lock);
+ h->writer->wr_audio(h->handle,buf);
+ pthread_mutex_unlock(&h->lock);
+ free(buf);
+ }
+ if (debug)
+ fprintf(stderr,"writer_audio_thread done\n");
+ return NULL;
+}
+
+/*
+ * with multiple compression threads we might receive
+ * the frames out-of-order
+ */
+static void*
+writer_video_thread(void *arg)
+{
+ struct movie_handle *h = arg;
+ struct ng_video_buf *buf;
+ struct ng_video_buf *reorder[REORDER_SIZE];
+ int seq,slot;
+
+ if (debug)
+ fprintf(stderr,"writer_video_thread start [pid=%d]\n",getpid());
+ seq = 0;
+ memset(&reorder,0,sizeof(reorder));
+ for (;;) {
+ buf = fifo_get(&h->vfifo);
+ if (NULL == buf)
+ break;
+ slot = buf->info.seq % REORDER_SIZE;
+ if (debug > 1)
+ fprintf(stderr,"video write: get seq=%d [%d]\n",
+ buf->info.seq,slot);
+ if (reorder[slot]) {
+ fprintf(stderr,"panic: reorder buffer full\n");
+ exit(1);
+ }
+ reorder[slot] = buf;
+
+ for (;;) {
+ slot = seq % REORDER_SIZE;
+ if (NULL == reorder[slot])
+ break;
+ buf = reorder[slot];
+ reorder[slot] = NULL;
+ if (debug > 1)
+ fprintf(stderr,"video write: put seq=%d [%d/%d]\n",
+ buf->info.seq,slot,seq);
+ seq++;
+
+ pthread_mutex_lock(&h->lock);
+ h->writer->wr_video(h->handle,buf);
+ if (buf->info.twice)
+ h->writer->wr_video(h->handle,buf);
+ pthread_mutex_unlock(&h->lock);
+ ng_release_video_buf(buf);
+ }
+ }
+ if (debug)
+ fprintf(stderr,"writer_video_thread done\n");
+ return NULL;
+}
+
+static void*
+record_audio_thread(void *arg)
+{
+ struct movie_handle *h = arg;
+ struct ng_audio_buf *buf;
+
+ if (debug)
+ fprintf(stderr,"record_audio_thread start [pid=%d]\n",getpid());
+ for (;;) {
+ buf = h->dsp->read(h->hdsp,h->stopby);
+ if (NULL == buf)
+ break;
+ if (0 == buf->size)
+ continue;
+ h->ats = buf->info.ts;
+ if (0 != fifo_put(&h->afifo,buf))
+ free(buf);
+ }
+ fifo_put(&h->afifo,NULL);
+ if (debug)
+ fprintf(stderr,"record_audio_thread done\n");
+ return NULL;
+}
+
+struct movie_handle*
+movie_writer_init(char *moviename, char *audioname,
+ const struct ng_writer *writer,
+ struct ng_video_fmt *video,const void *priv_video,int fps,
+ struct ng_audio_fmt *audio,const void *priv_audio,char *dsp,
+ int slots, int threads)
+{
+ struct movie_handle *h;
+ struct ng_video_conv *conv;
+ void *dummy;
+ int i;
+
+ if (debug)
+ fprintf(stderr,"movie_init_writer start\n");
+ h = malloc(sizeof(*h));
+ if (NULL == h)
+ return NULL;
+ memset(h,0,sizeof(*h));
+ pthread_mutex_init(&h->lock, NULL);
+ h->writer = writer;
+ h->slots = slots;
+
+ /* audio */
+ if (audio->fmtid != AUDIO_NONE) {
+ h->dsp = ng_dsp_open(dsp,audio,&h->hdsp);
+ if (NULL == h->dsp) {
+ free(h);
+ return NULL;
+ }
+ fifo_init(&h->afifo,"audio",slots,1);
+ pthread_create(&h->taudio,NULL,writer_audio_thread,h);
+ h->bytes_per_sec = ng_afmt_to_bits[audio->fmtid] *
+ ng_afmt_to_channels[audio->fmtid] * audio->rate / 8;
+ h->afmt = *audio;
+ }
+
+ /* video */
+ if (video->fmtid != VIDEO_NONE) {
+ if (0 == ng_grabber_setformat(video,1)) {
+ /* native format works -- no conversion needed */
+ fifo_init(&h->vfifo,"video",slots,1);
+ pthread_create(&h->tvideo,NULL,writer_video_thread,h);
+ } else {
+ /* have to convert video frames */
+ struct ng_video_fmt gfmt = *video;
+ if (NULL == (conv = ng_grabber_findconv(&gfmt,1))) {
+ if (h->afmt.fmtid != AUDIO_NONE)
+ h->dsp->close(h->hdsp);
+ free(h);
+ return NULL;
+ }
+ h->cthreads = threads;
+ if (h->cthreads < 1)
+ h->cthreads = 1;
+ if (h->cthreads > MAX_THREADS)
+ h->cthreads = MAX_THREADS;
+ fifo_init(&h->vfifo,"video",slots,h->cthreads);
+ fifo_init(&h->cfifo,"conv",slots,1);
+ pthread_create(&h->tvideo,NULL,writer_video_thread,h);
+ for (i = 0; i < h->cthreads; i++) {
+ h->hconv[i] = ng_convert_alloc(conv,&gfmt,video);
+ h->hconv[i]->in = &h->cfifo;
+ h->hconv[i]->out = &h->vfifo;
+ pthread_create(&h->tconv[i],NULL,ng_convert_thread,
+ h->hconv[i]);
+ }
+ }
+ h->vfmt = *video;
+ h->fps = fps;
+ }
+
+ /* open file */
+ h->handle = writer->wr_open(moviename,audioname,
+ video,priv_video,fps,
+ audio,priv_audio);
+ if (debug)
+ fprintf(stderr,"movie_init_writer end (h=%p)\n",h->handle);
+ if (NULL != h->handle)
+ return h;
+
+ /* Oops -- wr_open() didn't work. cleanup. */
+ if (h->afmt.fmtid != AUDIO_NONE) {
+ pthread_cancel(h->taudio);
+ pthread_join(h->taudio,&dummy);
+ h->dsp->close(h->hdsp);
+ }
+ if (h->vfmt.fmtid != VIDEO_NONE) {
+ pthread_cancel(h->tvideo);
+ pthread_join(h->tvideo,&dummy);
+ }
+ for (i = 0; i < h->cthreads; i++) {
+ pthread_cancel(h->tconv[i]);
+ pthread_join(h->tconv[i],&dummy);
+ }
+ free(h);
+ return NULL;
+}
+
+int
+movie_writer_start(struct movie_handle *h)
+{
+ int rc = 0;
+
+ if (debug)
+ fprintf(stderr,"movie_writer_start\n");
+ h->start = ng_get_timestamp();
+ if (h->afmt.fmtid != AUDIO_NONE)
+ if (0 != h->dsp->startrec(h->hdsp))
+ rc = -1;
+ if (h->vfmt.fmtid != VIDEO_NONE)
+ if (0 != drv->startvideo(h_drv,h->fps,h->slots))
+ rc = -1;
+ if (h->afmt.fmtid != AUDIO_NONE)
+ pthread_create(&h->raudio,NULL,record_audio_thread,h);
+ pthread_create(&h->tflush,NULL,flushit,NULL);
+ return rc;
+}
+
+int
+movie_writer_stop(struct movie_handle *h)
+{
+ char line[128];
+ long long stopby;
+ int frames,i;
+ void *dummy;
+
+ if (debug)
+ fprintf(stderr,"movie_writer_stop\n");
+
+ if (h->vfmt.fmtid != VIDEO_NONE && h->afmt.fmtid != AUDIO_NONE) {
+ for (frames = 0; frames < 16; frames++) {
+ stopby = (long long)(h->frames + frames) * 1000000000000 / h->fps;
+ if (stopby > h->ats)
+ break;
+ }
+ frames++;
+ h->stopby = (long long)(h->frames + frames) * 1000000000000 / h->fps;
+ while (frames) {
+ movie_grab_put_video(h,NULL);
+ frames--;
+ }
+ } else if (h->afmt.fmtid != AUDIO_NONE) {
+ h->stopby = h->ats;
+ }
+
+ /* send EOF */
+ if (h->cthreads)
+ fifo_put(&h->cfifo,NULL);
+ else
+ fifo_put(&h->vfifo,NULL);
+
+ /* join threads */
+ if (h->afmt.fmtid != AUDIO_NONE) {
+ pthread_join(h->raudio,&dummy);
+ pthread_join(h->taudio,&dummy);
+ }
+ if (h->vfmt.fmtid != VIDEO_NONE)
+ pthread_join(h->tvideo,&dummy);
+ for (i = 0; i < h->cthreads; i++)
+ pthread_join(h->tconv[i],&dummy);
+ pthread_cancel(h->tflush);
+ pthread_join(h->tflush,&dummy);
+
+ /* close file */
+ h->writer->wr_close(h->handle);
+ if (h->afmt.fmtid != AUDIO_NONE)
+ h->dsp->close(h->hdsp);
+ if (h->vfmt.fmtid != VIDEO_NONE)
+ drv->stopvideo(h_drv);
+
+ /* fifo stats */
+ sprintf(line, "fifo max fill: audio %d/%d, video %d/%d, convert %d/%d",
+ h->afifo.max,h->afifo.slots,
+ h->vfifo.max,h->vfifo.slots,
+ h->cfifo.max,h->cfifo.slots);
+ rec_status(line);
+
+ free(h);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+movie_print_timestamps(struct movie_handle *h)
+{
+ long long adiff,vdiff;
+ char line[128];
+
+ if (NULL == rec_status)
+ return;
+ h->rts = ng_get_timestamp() - h->start;
+ adiff = h->ats - h->rts;
+ vdiff = h->vts - h->rts;
+#if 0
+ sprintf(line,"real: %d.%03ds audio: %d.%03ds video: %d.%03ds",
+ (int)((h->rts / 1000000000)),
+ (int)((h->rts % 1000000000) / 1000000),
+ (int)((h->ats / 1000000000)),
+ (int)((h->ats % 1000000000) / 1000000),
+ (int)((h->vts / 1000000000)),
+ (int)((h->vts % 1000000000) / 1000000));
+#else
+ sprintf(line,"real: %d.%03ds audio: %c%d.%03ds video: %c%d.%03ds",
+ (int)((h->rts / 1000000000)),
+ (int)((h->rts % 1000000000) / 1000000),
+ (adiff > 0) ? '+' : '-',
+ (int)((abs(adiff) / 1000000000)),
+ (int)((abs(adiff) % 1000000000) / 1000000),
+ (vdiff > 0) ? '+' : '-',
+ (int)((abs(vdiff) / 1000000000)),
+ (int)((abs(vdiff) % 1000000000) / 1000000));
+#endif
+ rec_status(line);
+}
+
+int
+movie_grab_put_video(struct movie_handle *h, struct ng_video_buf **ret)
+{
+ struct ng_video_buf *buf;
+ int expected;
+
+ if (debug > 1)
+ fprintf(stderr,"grab_put_video\n");
+
+ /* fetch next frame */
+ buf = ng_grabber_grab_image(0);
+ if (NULL == buf)
+ return -1;
+#if 0 /* FIXME */
+ buf = ng_filter_single(cur_filter,buf);
+#endif
+
+ /* rate control */
+ expected = buf->info.ts * h->fps / 1000000000000;
+ if (expected < h->frames) {
+ if (debug > 1)
+ fprintf(stderr,"rate: ignoring frame\n");
+ ng_release_video_buf(buf);
+ return 0;
+ }
+ if (expected > h->frames) {
+ fprintf(stderr,"rate: queueing frame twice (%d)\n",
+ expected-h->frames);
+ buf->info.twice++;
+ h->frames++;
+ }
+ h->frames++;
+ h->vts = buf->info.ts;
+ buf->info.seq = h->seq++;
+
+ /* return a pointer to the frame if requested */
+ if (NULL != ret) {
+ buf->refcount++;
+ *ret = buf;
+ }
+
+ /* put into fifo */
+ if (h->cthreads) {
+ if (0 != fifo_put(&h->cfifo,buf))
+ ng_release_video_buf(buf);
+ } else {
+ if (0 != fifo_put(&h->vfifo,buf))
+ ng_release_video_buf(buf);
+ }
+
+ /* feedback */
+ movie_print_timestamps(h);
+ return h->frames;
+}
diff --git a/common/capture.h b/common/capture.h
new file mode 100644
index 0000000..55eba30
--- /dev/null
+++ b/common/capture.h
@@ -0,0 +1,52 @@
+#ifndef CAPTURE_H
+#define CAPTURE_H
+
+#define FIFO_MAX 64
+
+struct FIFO {
+ char *name;
+ unsigned char *data[FIFO_MAX];
+ int slots,read,write,eof,max,writers;
+ pthread_mutex_t lock;
+ pthread_cond_t hasdata;
+};
+
+void fifo_init(struct FIFO *fifo, char *name, int slots, int writers);
+int fifo_put(struct FIFO *fifo, void *data);
+void* fifo_get(struct FIFO *fifo);
+
+
+struct ng_convert_handle;
+struct ng_convert_handle* ng_convert_alloc(struct ng_video_conv *conv,
+ struct ng_video_fmt *i,
+ struct ng_video_fmt *o);
+
+void ng_convert_init(struct ng_convert_handle *h);
+struct ng_video_buf* ng_convert_frame(struct ng_convert_handle *h,
+ struct ng_video_buf *dest,
+ struct ng_video_buf *buf);
+void ng_convert_fini(struct ng_convert_handle *h);
+struct ng_video_buf* ng_convert_single(struct ng_convert_handle *h,
+ struct ng_video_buf *in);
+void* ng_convert_thread(void *arg);
+
+int ng_grabber_setformat(struct ng_video_fmt *fmt, int fix_ratio);
+struct ng_video_conv* ng_grabber_findconv(struct ng_video_fmt *fmt,
+ int fix_ratio);
+struct ng_video_buf* ng_grabber_grab_image(int single);
+struct ng_video_buf* ng_grabber_get_image(struct ng_video_fmt *fmt);
+
+
+struct movie_handle*
+movie_writer_init(char *moviename, char *audioname,
+ const struct ng_writer *writer,
+ struct ng_video_fmt *video,const void *priv_video,int fps,
+ struct ng_audio_fmt *audio,const void *priv_audio,char *dsp,
+ int slots, int threads);
+int movie_writer_start(struct movie_handle*);
+int movie_writer_stop(struct movie_handle*);
+
+int movie_grab_put_video(struct movie_handle*, struct ng_video_buf **ret);
+int movie_grab_put_audio(struct movie_handle*);
+
+#endif /* CAPTURE_H */
diff --git a/common/channel.c b/common/channel.c
new file mode 100644
index 0000000..57c3750
--- /dev/null
+++ b/common/channel.c
@@ -0,0 +1,714 @@
+/*
+ channel for Bt848 frame grabber driver
+
+ Copyright (C) 1996,97 Marcus Metzler (mocm@thp.uni-koeln.de)
+
+ many changes by Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ [ hmm, think by now nearly nothing left from the original code ... ]
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <pthread.h>
+
+#ifndef NO_X11
+# include <X11/Xlib.h>
+# include <X11/Intrinsic.h>
+# include <X11/StringDefs.h>
+# include <X11/Xaw/XawInit.h>
+# include <X11/Xaw/Command.h>
+# include <X11/Xaw/Paned.h>
+#endif
+
+#include "grab-ng.h"
+#include "channel.h"
+#include "commands.h"
+#include "frequencies.h"
+#include "sound.h"
+#include "parseconfig.h"
+#include "event.h"
+
+/* ----------------------------------------------------------------------- */
+/* misc common stuff, not only channel related */
+
+struct CHANNEL defaults = {
+ name: "defaults",
+ capture: CAPTURE_ON,
+ channel: -1,
+ audio: -1,
+ color: -1,
+ bright: -1,
+ hue: -1,
+ contrast: -1,
+};
+
+struct CHANNEL **channels = NULL;
+int count = 0;
+int alloc_count = 0;
+
+int last_sender = -1, cur_sender = -1, cur_channel = -1, cur_fine = 0;
+int cur_freq;
+struct ng_filter *cur_filter;
+
+int cur_capture = CAPTURE_OFF;
+int have_config;
+int keypad_ntsc = 0;
+int keypad_partial = 1;
+int use_osd = 1;
+int fs_width,fs_height,fs_xoff,fs_yoff;
+int pix_width=128, pix_height=96, pix_cols=1;
+
+char *mov_driver = NULL;
+char *mov_video = NULL;
+char *mov_fps = NULL;
+char *mov_audio = NULL;
+char *mov_rate = NULL;
+
+#ifndef NO_X11
+extern Widget chan_box, chan_viewport, tv, opt_paned, launch_paned;
+#endif
+
+static char *mixer = NULL;
+char mixerdev[32],mixerctl[16];
+char *midi = NULL;
+
+struct LAUNCH *launch = NULL;
+int nlaunch = 0;
+
+/* ----------------------------------------------------------------------- */
+
+int lookup_channel(char *channel)
+{
+ int i,nr1,nr2;
+ char tag1[5],tag2[5];
+
+ if (NULL == channel)
+ return -1;
+
+ if (isdigit(channel[0])) {
+ tag1[0] = 0;
+ nr1 = atoi(channel);
+ } else {
+ sscanf(channel,"%4[A-Za-z]%d",tag1,&nr1);
+ }
+
+ for (i = 0; i < chancount; i++) {
+ if (isdigit(chanlist[i].name[0])) {
+ tag2[0] = 0;
+ nr2 = atoi(chanlist[i].name);
+ } else {
+ sscanf(chanlist[i].name,"%4[A-Za-z]%d",tag2,&nr2);
+ }
+ if (tag1[0] && tag2[0])
+ if (nr1 == nr2 && 0 == strcmp(tag1,tag2))
+ break;
+ if (!tag1[0] && !tag2[0])
+ if (nr1 == nr2)
+ break;
+ }
+ if (i == chancount)
+ return -1;
+
+ return i;
+}
+
+int get_freq(int i)
+{
+ if (i < 0 || i >= chancount)
+ return -1;
+ return chanlist[i].freq*16/1000;
+}
+
+int cf2freq(char *name, int fine)
+{
+ int i;
+
+ if (-1 == (i = lookup_channel(name)))
+ return -1;
+ return get_freq(i)+fine;
+}
+
+/* ----------------------------------------------------------------------- */
+
+struct STRTAB captab[] = {
+ { CAPTURE_ON, "on" },
+ { CAPTURE_ON, "yes" },
+ { CAPTURE_ON, "true" },
+ { CAPTURE_OFF, "off" },
+ { CAPTURE_OFF, "no" },
+ { CAPTURE_OFF, "false" },
+ { CAPTURE_OVERLAY, "over" },
+ { CAPTURE_OVERLAY, "overlay" },
+ { CAPTURE_GRABDISPLAY, "grab" },
+ { CAPTURE_GRABDISPLAY, "grabdisplay" },
+ { -1, NULL, },
+};
+
+/* just malloc memory for a new channel ... */
+struct CHANNEL*
+add_channel(char *name)
+{
+ struct CHANNEL *channel;
+
+ if (alloc_count == count) {
+ alloc_count += 16;
+ if (alloc_count == 16)
+ channels = malloc(sizeof(struct CHANNEL*)*alloc_count);
+ else
+ channels = realloc(channels,sizeof(struct CHANNEL*)*alloc_count);
+ }
+ channel = channels[count++] = malloc(sizeof(struct CHANNEL));
+ memcpy(channel,&defaults,sizeof(struct CHANNEL));
+ channel->name = strdup(name);
+ return channel;
+}
+
+#ifndef NO_X11
+
+#define PANED_FIX \
+ XtNallowResize, False, \
+ XtNshowGrip, False, \
+ XtNskipAdjust, True
+
+void hotkey_channel(struct CHANNEL *channel)
+{
+ char str[100],key[32],ctrl[16];
+
+ if (NULL == channel->key)
+ return;
+ if (2 == sscanf(channel->key,"%15[A-Za-z0-9_]+%31[A-Za-z0-9_]",
+ ctrl,key))
+ sprintf(str,"%s<Key>%s: Command(setstation,\"%s\")",
+ ctrl,key,channel->name);
+ else
+ sprintf(str,"<Key>%s: Command(setstation,\"%s\")",
+ channel->key,channel->name);
+ XtOverrideTranslations(tv,XtParseTranslationTable(str));
+ XtOverrideTranslations(opt_paned,XtParseTranslationTable(str));
+ XtOverrideTranslations(chan_viewport,XtParseTranslationTable(str));
+}
+
+static void
+launch_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ char *argv[2];
+
+ argv[0] = (char*)clientdata;
+ argv[1] = NULL;
+ XtCallActionProc(widget,"Launch",NULL,argv,1);
+}
+
+static void
+hotkey_launch(struct LAUNCH *launch)
+{
+ Widget c;
+ char str[100],key[32],ctrl[16],label[64];
+
+ if (NULL == launch->key)
+ return;
+ if (2 == sscanf(launch->key,"%15[A-Za-z0-9_]+%31[A-Za-z0-9_]",
+ ctrl,key))
+ sprintf(str,"%s<Key>%s: Launch(\"%s\")",ctrl,key,launch->name);
+ else
+ sprintf(str,"<Key>%s: Launch(\"%s\")",launch->key,launch->name);
+ XtOverrideTranslations(tv,XtParseTranslationTable(str));
+ XtOverrideTranslations(opt_paned,XtParseTranslationTable(str));
+ XtOverrideTranslations(chan_viewport,XtParseTranslationTable(str));
+
+ sprintf(label,"%-20s %s",launch->name,launch->key);
+ c = XtVaCreateManagedWidget(launch->name, commandWidgetClass,
+ launch_paned,
+ PANED_FIX,
+ XtNlabel,label,
+ NULL);
+ XtAddCallback(c,XtNcallback,launch_cb,(XtPointer)(launch->name));
+}
+
+static void
+button_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ struct CHANNEL *channel = clientdata;
+ do_va_cmd(2,"setstation",channel->name);
+}
+
+/* ... and initalize later */
+void configure_channel(struct CHANNEL *channel)
+{
+ channel->button =
+ XtVaCreateManagedWidget(channel->name,
+ commandWidgetClass, chan_box,
+ XtNwidth,pix_width,
+ XtNheight,pix_height,
+ NULL);
+ XtAddCallback(channel->button,XtNcallback,button_cb,(XtPointer*)channel);
+ hotkey_channel(channel);
+}
+#endif
+
+/* delete channel */
+void
+del_channel(int i)
+{
+ free(channels[i]->name);
+ if (channels[i]->key)
+ free(channels[i]->key);
+ free(channels[i]);
+ count--;
+ if (i < count)
+ memmove(channels+i,channels+i+1,(count-i)*sizeof(struct CHANNEL*));
+}
+
+void
+calc_frequencies()
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (NULL == channels[i]->cname)
+ continue;
+ channels[i]->channel = lookup_channel(channels[i]->cname);
+ if (-1 == channels[i]->channel)
+ channels[i]->freq = -1;
+ else
+ channels[i]->freq = get_freq(channels[i]->channel)
+ + channels[i]->fine;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void
+init_channel(char *name, struct CHANNEL *c)
+{
+ struct ng_attribute *attr;
+ char *val; int n,i;
+
+ if (NULL != (val = cfg_get_str(name,"capture"))) {
+ if (-1 != (i = str_to_int(val,captab)))
+ c->capture = i;
+ else
+ fprintf(stderr,"config: invalid value for capture: %s\n",val);
+ }
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_INPUT)) &&
+ (NULL != (val = cfg_get_str(name,"input")) ||
+ NULL != (val = cfg_get_str(name,"source")))) { /* obsolete */
+ if (-1 != (i = ng_attr_getint(attr,val)))
+ c->input = i;
+ else {
+ fprintf(stderr,"config: invalid value for input: %s\n",val);
+ ng_attr_listchoices(attr);
+ }
+ }
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_NORM)) &&
+ NULL != (val = cfg_get_str(name,"norm"))) {
+ if (-1 != (i = ng_attr_getint(attr,val)))
+ c->norm = i;
+ else {
+ fprintf(stderr,"config: invalid value for norm: %s\n",val);
+ ng_attr_listchoices(attr);
+ }
+ }
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_AUDIO_MODE)) &&
+ NULL != (val = cfg_get_str(name,"audio"))) {
+ if (-1 != (i = ng_attr_getint(attr,val)))
+ c->audio = i;
+ else {
+ fprintf(stderr,"config: invalid value for audio: %s\n",val);
+ ng_attr_listchoices(attr);
+ }
+ }
+
+ if (NULL != (val = cfg_get_str(name,"channel")))
+ c->cname = strdup(val);
+ if (NULL != (val = cfg_get_str(name,"freq")))
+ c->freq = (int)(atof(val)*16);
+ if (0 != (n = cfg_get_signed_int(name,"fine")))
+ c->fine = n;
+
+ if (NULL != (val = cfg_get_str(name,"key")))
+ c->key = strdup(val);
+ if (NULL != (val = cfg_get_str(name,"midi")))
+ c->midi = atoi(val);
+
+ attr = ng_attr_byid(attrs,ATTR_ID_COLOR);
+ if (attr && NULL != (val = cfg_get_str(name,"color")))
+ c->color = ng_attr_parse_int(attr,val);
+ attr = ng_attr_byid(attrs,ATTR_ID_BRIGHT);
+ if (attr && NULL != (val = cfg_get_str(name,"bright")))
+ c->bright = ng_attr_parse_int(attr,val);
+ attr = ng_attr_byid(attrs,ATTR_ID_HUE);
+ if (attr && NULL != (val = cfg_get_str(name,"hue")))
+ c->hue = ng_attr_parse_int(attr,val);
+ attr = ng_attr_byid(attrs,ATTR_ID_CONTRAST);
+ if (attr && NULL != (val = cfg_get_str(name,"contrast")))
+ c->contrast = ng_attr_parse_int(attr,val);
+}
+
+void
+read_config(char *conffile)
+{
+ char filename[100];
+ char *val;
+ int i;
+
+ if (conffile) {
+ if (0 == cfg_parse_file(conffile))
+ have_config = 1;
+ } else {
+ sprintf(filename,"%s/%s",getenv("HOME"),".xawtv");
+ if (0 == cfg_parse_file(CONFIGFILE))
+ have_config = 1;
+ if (0 == cfg_parse_file(filename))
+ have_config = 1;
+ }
+
+ /* misc global settings */
+ if (NULL != (val = cfg_get_str("global","mixer"))) {
+ mixer = strdup(val);
+ if (2 != sscanf(mixer,"%31[^:]:%15s",mixerdev,mixerctl)) {
+ strcpy(mixerdev,ng_dev.mixer);
+ strncpy(mixerctl,val,15);
+ mixerctl[15] = 0;
+ }
+ }
+
+ if (NULL != (val = cfg_get_str("global","midi")))
+ midi = strdup(val);
+
+ if (NULL != (val = cfg_get_str("global","freqtab"))) {
+ for (i = 0; chanlists[i].name != NULL; i++)
+ if (0 == strcasecmp(val,chanlists[i].name))
+ break;
+ if (chanlists[i].name != NULL) {
+ chantab = i;
+ chanlist = chanlists[chantab].list;
+ chancount = chanlists[chantab].count;
+ } else
+ fprintf(stderr,"invalid value for freqtab: %s\n",val);
+ }
+
+ if (NULL != (val = cfg_get_str("global","fullscreen"))) {
+ if (2 != sscanf(val,"%d x %d",&fs_width,&fs_height)) {
+ fprintf(stderr,"invalid value for fullscreen: %s\n",val);
+ fs_width = fs_height = 0;
+ }
+ }
+
+ if (NULL != (val = cfg_get_str("global","pixsize"))) {
+ if (2 != sscanf(val,"%d x %d",&pix_width,&pix_height)) {
+ fprintf(stderr,"invalid value for pixsize: %s\n",val);
+ pix_width = 128;
+ pix_height = 96;
+ }
+ }
+ if (-1 != (i = cfg_get_int("global","pixcols")))
+ pix_cols = i;
+
+ if (NULL != (val = cfg_get_str("global","wm-off-by"))) {
+ if (2 != sscanf(val,"%d %d",&fs_xoff,&fs_yoff)) {
+ fprintf(stderr,"invalid value for wm-off-by: %s\n",val);
+ fs_xoff = fs_yoff = 0;
+ }
+ }
+ if (NULL != (val = cfg_get_str("global","ratio"))) {
+ if (2 != sscanf(val,"%d:%d",&ng_ratio_x,&ng_ratio_y)) {
+ fprintf(stderr,"invalid value for ratio: %s\n",val);
+ ng_ratio_x = ng_ratio_y = 0;
+ }
+ }
+
+ if (-1 != (i = cfg_get_int("global","jpeg-quality")))
+ ng_jpeg_quality = i;
+
+ if (NULL != (val = cfg_get_str("global","keypad-ntsc")))
+ if (-1 != (i = str_to_int(val,booltab)))
+ keypad_ntsc = i;
+ if (NULL != (val = cfg_get_str("global","keypad-partial")))
+ if (-1 != (i = str_to_int(val,booltab)))
+ keypad_partial = i;
+ if (NULL != (val = cfg_get_str("global","osd")))
+ if (-1 != (i = str_to_int(val,booltab)))
+ use_osd = i;
+
+ if (NULL != (val = cfg_get_str("global","mov-driver")))
+ mov_driver = val;
+ if (NULL != (val = cfg_get_str("global","mov-video")))
+ mov_video = val;
+ if (NULL != (val = cfg_get_str("global","mov-fps")))
+ mov_fps = val;
+ if (NULL != (val = cfg_get_str("global","mov-audio")))
+ mov_audio = val;
+ if (NULL != (val = cfg_get_str("global","mov-rate")))
+ mov_rate = val;
+}
+
+void
+parse_config(void)
+{
+ char key[16], cmdline[128];
+ char **list,*val;
+#ifndef NO_X11
+ int i;
+#endif
+
+ /* launch */
+ list = cfg_list_entries("launch");
+ if (NULL != list) {
+ for (; *list != NULL; list++) {
+ if (NULL != (val = cfg_get_str("launch",*list)) &&
+ 2 == sscanf(val,"%15[^,], %127[^\n]",
+ key,cmdline)) {
+ launch = realloc(launch,sizeof(struct LAUNCH)*(nlaunch+1));
+ launch[nlaunch].name = strdup(*list);
+ launch[nlaunch].key = strdup(key);
+ launch[nlaunch].cmdline = strdup(cmdline);
+#ifndef NO_X11
+ hotkey_launch(launch+nlaunch);
+#endif
+ nlaunch++;
+ } else {
+ fprintf(stderr,"invalid value in section [launch]: %s\n",val);
+ }
+ }
+ }
+
+ /* events */
+ event_readconfig();
+
+ /* channels */
+ init_channel("defaults",&defaults);
+ for (list = cfg_list_sections(); *list != NULL; list++) {
+ if (0 == strcmp(*list,"defaults")) continue;
+ if (0 == strcmp(*list,"global")) continue;
+ if (0 == strcmp(*list,"launch")) continue;
+ if (0 == strcmp(*list,"eventmap")) continue;
+ init_channel(*list,add_channel(*list));
+ }
+
+ /* calculate channel frequencies */
+ defaults.channel = lookup_channel(defaults.cname);
+ defaults.freq = get_freq(defaults.channel) + defaults.fine;
+ calc_frequencies();
+#ifndef NO_X11
+ for (i = 0; i < count; i++)
+ configure_channel(channels[i]);
+#endif
+}
+
+/* ----------------------------------------------------------------------- */
+
+void
+save_config()
+{
+ struct ng_attribute *attr;
+ char filename1[100], filename2[100];
+ FILE *fp;
+ int i;
+
+ sprintf(filename1,"%s/%s",getenv("HOME"),".xawtv");
+ sprintf(filename2,"%s/%s",getenv("HOME"),".xawtv~");
+
+ /* delete old backup */
+ unlink(filename2);
+
+ /* current becomes backup */
+ if (0 == link(filename1,filename2))
+ unlink(filename1);
+
+ /* write new one... */
+ fp = fopen(filename1,"w");
+ if (NULL == fp) {
+ fprintf(stderr,"can't open config file %s\n",filename1);
+ return;
+ }
+
+ fprintf(fp,"[global]\n");
+ if (fs_width && fs_height)
+ fprintf(fp,"fullscreen = %d x %d\n",fs_width,fs_height);
+ if (fs_xoff || fs_yoff)
+ fprintf(fp,"wm-off-by = %+d%+d\n",fs_xoff,fs_yoff);
+ if (ng_ratio_x || ng_ratio_y)
+ fprintf(fp,"ratio = %d:%d\n",ng_ratio_x,ng_ratio_y);
+ fprintf(fp,"freqtab = %s\n",chanlists[chantab].name);
+ fprintf(fp,"pixsize = %d x %d\n",pix_width,pix_height);
+ fprintf(fp,"pixcols = %d\n",pix_cols);
+ fprintf(fp,"jpeg-quality = %d\n",ng_jpeg_quality);
+ fprintf(fp,"keypad-ntsc = %s\n",int_to_str(keypad_ntsc,booltab));
+ fprintf(fp,"keypad-partial = %s\n",int_to_str(keypad_partial,booltab));
+ fprintf(fp,"osd = %s\n",int_to_str(use_osd,booltab));
+ if (mixer)
+ fprintf(fp,"mixer = %s\n",mixer);
+ if (midi)
+ fprintf(fp,"midi = %s\n",midi);
+
+ if (mov_driver)
+ fprintf(fp,"mov-driver = %s\n",mov_driver);
+ if (mov_video)
+ fprintf(fp,"mov-video = %s\n",mov_video);
+ if (mov_fps)
+ fprintf(fp,"mov-fps = %s\n",mov_fps);
+ if (mov_audio)
+ fprintf(fp,"mov-audio = %s\n",mov_audio);
+ if (mov_rate)
+ fprintf(fp,"mov-rate = %s\n",mov_rate);
+
+ fprintf(fp,"\n");
+
+ if (nlaunch > 0) {
+ fprintf(fp,"[launch]\n");
+ for (i = 0; i < nlaunch; i++) {
+ fprintf(fp,"%s = %s, %s\n",
+ launch[i].name,launch[i].key,launch[i].cmdline);
+ }
+ fprintf(fp,"\n");
+ }
+
+ /* events */
+ event_writeconfig(fp);
+
+ /* write help */
+ fprintf(fp,"# [Station name]\n");
+ fprintf(fp,"# capture = overlay | grabdisplay | on | off\n");
+ fprintf(fp,"# input = Television | Composite1 | S-Video | ...\n");
+ fprintf(fp,"# norm = PAL | NTSC | SECAM | ... \n");
+ fprintf(fp,"# channel = #\n");
+ fprintf(fp,"# fine = # (-128..+127)\n");
+ fprintf(fp,"# key = keysym | modifier+keysym\n");
+ fprintf(fp,"# color = #\n");
+ fprintf(fp,"# bright = #\n");
+ fprintf(fp,"# hue = #\n");
+ fprintf(fp,"# contrast = #\n");
+ fprintf(fp,"\n");
+
+ /* write defaults */
+ fprintf(fp,"[defaults]\n");
+ fprintf(fp,"norm = %s\n",
+ ng_attr_getstr(ng_attr_byid(attrs,ATTR_ID_NORM),
+ cur_attrs[ATTR_ID_NORM]));
+ fprintf(fp,"input = %s\n",
+ ng_attr_getstr(ng_attr_byid(attrs,ATTR_ID_INPUT),
+ cur_attrs[ATTR_ID_INPUT]));
+ fprintf(fp,"capture = %s\n",int_to_str(cur_capture,captab));
+
+ attr = ng_attr_byid(attrs,ATTR_ID_COLOR);
+ if (attr && attr->defval != cur_attrs[ATTR_ID_COLOR])
+ fprintf(fp,"color = %d%%\n",
+ ng_attr_int2percent(attr,cur_attrs[ATTR_ID_COLOR]));
+ attr = ng_attr_byid(attrs,ATTR_ID_BRIGHT);
+ if (attr && attr->defval != cur_attrs[ATTR_ID_BRIGHT])
+ fprintf(fp,"bright = %d%%\n",
+ ng_attr_int2percent(attr,cur_attrs[ATTR_ID_BRIGHT]));
+ attr = ng_attr_byid(attrs,ATTR_ID_HUE);
+ if (attr && attr->defval != cur_attrs[ATTR_ID_HUE])
+ fprintf(fp,"hue = %d%%\n",
+ ng_attr_int2percent(attr,cur_attrs[ATTR_ID_HUE]));
+ attr = ng_attr_byid(attrs,ATTR_ID_CONTRAST);
+ if (attr && attr->defval != cur_attrs[ATTR_ID_CONTRAST])
+ fprintf(fp,"contrast = %d%%\n",
+ ng_attr_int2percent(attr,cur_attrs[ATTR_ID_CONTRAST]));
+ fprintf(fp,"\n");
+
+ /* write channels */
+ for (i = 0; i < count; i++) {
+ fprintf(fp,"[%s]\n",channels[i]->name);
+ if (0 != strcmp(channels[i]->cname,"none")) {
+ fprintf(fp,"channel = %s\n",chanlist[channels[i]->channel].name);
+ if (0 != channels[i]->fine)
+ fprintf(fp,"fine = %+d\n", channels[i]->fine);
+ } else {
+ fprintf(fp,"freq = %.2f\n",(float)(channels[i]->freq)/16);
+ }
+
+ if ( channels[i]->norm != cur_attrs[ATTR_ID_NORM])
+ fprintf(fp,"norm = %s\n",
+ ng_attr_getstr(ng_attr_byid(attrs,ATTR_ID_NORM),
+ channels[i]->norm));
+ if (channels[i]->input != cur_attrs[ATTR_ID_INPUT])
+ fprintf(fp,"input = %s\n",
+ ng_attr_getstr(ng_attr_byid(attrs,ATTR_ID_INPUT),
+ channels[i]->input));
+ if (channels[i]->key != NULL)
+ fprintf(fp,"key = %s\n",channels[i]->key);
+ if (channels[i]->midi != 0)
+ fprintf(fp,"midi = %d\n",channels[i]->midi);
+ if (channels[i]->capture != cur_capture)
+ fprintf(fp,"capture = %s\n",
+ int_to_str(channels[i]->capture,captab));
+
+ attr = ng_attr_byid(attrs,ATTR_ID_COLOR);
+ if (attr && cur_attrs[ATTR_ID_COLOR] != channels[i]->color)
+ fprintf(fp,"color = %d%%\n",
+ ng_attr_int2percent(attr,channels[i]->color));
+ attr = ng_attr_byid(attrs,ATTR_ID_BRIGHT);
+ if (attr && cur_attrs[ATTR_ID_BRIGHT] != channels[i]->bright)
+ fprintf(fp,"bright = %d%%\n",
+ ng_attr_int2percent(attr,channels[i]->bright));
+ attr = ng_attr_byid(attrs,ATTR_ID_HUE);
+ if (attr && cur_attrs[ATTR_ID_HUE] != channels[i]->hue)
+ fprintf(fp,"hue = %d%%\n",
+ ng_attr_int2percent(attr,channels[i]->hue));
+ attr = ng_attr_byid(attrs,ATTR_ID_CONTRAST);
+ if (attr && cur_attrs[ATTR_ID_CONTRAST] != channels[i]->contrast)
+ fprintf(fp,"contrast = %d%%\n",
+ ng_attr_int2percent(attr,channels[i]->contrast));
+
+ fprintf(fp,"\n");
+ }
+ fclose(fp);
+}
+
+/* ----------------------------------------------------------------------- */
+
+struct STRTAB booltab[] = {
+ { 0, "no" },
+ { 0, "false" },
+ { 0, "off" },
+ { 1, "yes" },
+ { 1, "true" },
+ { 1, "on" },
+ { -1, NULL }
+};
+
+int
+str_to_int(char *str, struct STRTAB *tab)
+{
+ int i;
+
+ if (str[0] >= '0' && str[0] <= '9')
+ return atoi(str);
+ for (i = 0; tab[i].str != NULL; i++)
+ if (0 == strcasecmp(str,tab[i].str))
+ return(tab[i].nr);
+ return -1;
+}
+
+const char*
+int_to_str(int n, struct STRTAB *tab)
+{
+ int i;
+
+ for (i = 0; tab[i].str != NULL; i++)
+ if (tab[i].nr == n)
+ return tab[i].str;
+ return NULL;
+}
diff --git a/common/channel.h b/common/channel.h
new file mode 100644
index 0000000..b5b04f1
--- /dev/null
+++ b/common/channel.h
@@ -0,0 +1,94 @@
+#ifndef X_DISPLAY_MISSING
+# include <X11/Xlib.h>
+# include <X11/Intrinsic.h>
+#endif
+
+#define CAPTURE_OFF 0
+#define CAPTURE_OVERLAY 1
+#define CAPTURE_GRABDISPLAY 2
+#define CAPTURE_ON 9
+
+struct CHANNEL {
+ char *name;
+ char *key;
+ int midi;
+
+ char *cname; /* name of the channel */
+ int channel; /* index into tvtuner[] */
+ int fine;
+ int freq;
+ int audio;
+
+ int capture;
+ int input;
+ int norm;
+
+ int color;
+ int bright;
+ int hue;
+ int contrast;
+
+#ifndef X_DISPLAY_MISSING
+ /* FIXME */
+ Pixmap pixmap;
+ Widget button;
+#endif
+};
+
+extern struct CHANNEL defaults;
+extern struct CHANNEL **channels;
+extern int count;
+
+extern int have_config;
+extern int jpeg_quality;
+extern int keypad_ntsc;
+extern int keypad_partial;
+extern int use_osd;
+extern int fs_width,fs_height,fs_xoff,fs_yoff;
+extern int pix_width,pix_height,pix_cols;
+extern int last_sender, cur_sender;
+extern int cur_channel, cur_fine;
+extern int cur_capture, cur_freq;
+extern struct ng_filter *cur_filter;
+
+extern char *mov_driver;
+extern char *mov_video;
+extern char *mov_fps;
+extern char *mov_audio;
+extern char *mov_rate;
+
+extern char mixerdev[32],mixerctl[16];
+extern char *midi;
+
+int lookup_channel(char *channel);
+int get_freq(int i);
+int cf2freq(char *name, int fine);
+
+struct CHANNEL* add_channel(char *name);
+void hotkey_channel(struct CHANNEL *channel);
+void configure_channel(struct CHANNEL *channel);
+void del_channel(int nr);
+void calc_frequencies(void);
+
+void read_config(char *conffile);
+void parse_config(void);
+void save_config(void);
+
+/* ----------------------------------------------------------------------- */
+
+struct LAUNCH {
+ char *name;
+ char *key;
+ char *cmdline;
+};
+
+extern struct LAUNCH *launch;
+extern int nlaunch;
+
+/* ----------------------------------------------------------------------- */
+
+extern struct STRTAB booltab[];
+extern struct STRTAB captab[];
+
+int str_to_int(char *str, struct STRTAB *tab);
+const char* int_to_str(int n, struct STRTAB *tab);
diff --git a/common/commands.c b/common/commands.c
new file mode 100644
index 0000000..846c782
--- /dev/null
+++ b/common/commands.c
@@ -0,0 +1,1310 @@
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <time.h>
+#include <pthread.h>
+
+#include "grab-ng.h"
+
+#include "capture.h"
+#include "commands.h"
+#include "writefile.h"
+#include "channel.h"
+#include "webcam.h"
+#include "frequencies.h"
+#include "sound.h"
+
+/* ----------------------------------------------------------------------- */
+
+/* feedback for the user */
+void (*update_title)(char *message);
+void (*display_message)(char *message);
+void (*vtx_message)(struct TEXTELEM *tt);
+void (*rec_status)(char *message);
+
+/* for updating GUI elements / whatever */
+void (*attr_notify)(struct ng_attribute *attr, int val);
+void (*volume_notify)(void);
+void (*freqtab_notify)(void);
+void (*setfreqtab_notify)(void);
+void (*setstation_notify)(void);
+
+/* gets called _before_ channel switches */
+void (*channel_switch_hook)(void);
+
+/* capture overlay/grab/off */
+void (*set_capture_hook)(int old, int new, int tmp_switch);
+
+/* toggle fullscreen */
+void (*fullscreen_hook)(void);
+void (*exit_hook)(void);
+void (*capture_get_hook)(void);
+void (*capture_rel_hook)(void);
+void (*movie_hook)(int argc, char **argv);
+
+int debug;
+int do_overlay;
+char *snapbase = "snap";
+int have_shmem;
+
+struct ng_video_fmt x11_fmt;
+int cur_movie,cur_attrs[256];
+
+/* current hardware driver */
+const struct ng_vid_driver *drv;
+void *h_drv;
+int f_drv;
+
+struct ng_attribute *attrs = NULL;
+
+
+/* ----------------------------------------------------------------------- */
+
+static int setfreqtab_handler(char *name, int argc, char **argv);
+static int setstation_handler(char *name, int argc, char **argv);
+static int setchannel_handler(char *name, int argc, char **argv);
+
+static int capture_handler(char *name, int argc, char **argv);
+static int volume_handler(char *name, int argc, char **argv);
+static int attr_handler(char *name, int argc, char **argv);
+static int show_handler(char *name, int argc, char **argv);
+static int list_handler(char *name, int argc, char **argv);
+static int dattr_handler(char *name, int argc, char **argv);
+
+static int snap_handler(char *name, int argc, char **argv);
+static int webcam_handler(char *name, int argc, char **argv);
+static int movie_handler(char *name, int argc, char **argv);
+static int fullscreen_handler(char *name, int argc, char **argv);
+static int msg_handler(char *name, int argc, char **argv);
+static int showtime_handler(char *name, int argc, char **argv);
+static int vtx_handler(char *name, int argc, char **argv);
+static int exit_handler(char *name, int argc, char **argv);
+
+static int keypad_handler(char *name, int argc, char **argv);
+
+static struct COMMANDS {
+ char *name;
+ int min_args;
+ int (*handler)(char *name, int argc, char **argv);
+} commands[] = {
+ { "setstation", 0, setstation_handler },
+ { "setchannel", 0, setchannel_handler },
+ { "setfreqtab", 1, setfreqtab_handler },
+
+ { "capture", 1, capture_handler },
+
+ { "setnorm", 1, attr_handler },
+ { "setinput", 1, attr_handler },
+ { "setattr", 1, attr_handler },
+ { "color", 0, attr_handler },
+ { "hue", 0, attr_handler },
+ { "bright", 0, attr_handler },
+ { "contrast", 0, attr_handler },
+ { "show", 0, show_handler },
+ { "list", 0, list_handler },
+
+ { "volume", 0, volume_handler },
+ { "attr", 0, dattr_handler },
+
+ { "snap", 0, snap_handler },
+ { "webcam", 1, webcam_handler },
+ { "movie", 1, movie_handler },
+ { "fullscreen", 0, fullscreen_handler },
+ { "msg", 1, msg_handler },
+ { "vtx", 0, vtx_handler },
+ { "message", 0, msg_handler },
+ { "exit", 0, exit_handler },
+ { "quit", 0, exit_handler },
+ { "bye", 0, exit_handler },
+
+ { "keypad", 1, keypad_handler },
+ { "showtime", 0, showtime_handler },
+
+ { NULL, 0, NULL }
+};
+
+static int cur_dattr = 0;
+static int dattr[] = {
+ ATTR_ID_VOLUME,
+ ATTR_ID_BRIGHT,
+ ATTR_ID_CONTRAST,
+ ATTR_ID_COLOR,
+ ATTR_ID_HUE
+};
+#define NUM_DATTR (sizeof(dattr)/sizeof(char*))
+
+static int keypad_state = -1;
+
+/* ----------------------------------------------------------------------- */
+
+void add_attrs(struct ng_attribute *new)
+{
+ struct ng_attribute *all;
+ int nold,nnew;
+
+ if (attrs)
+ for (nold = 0; attrs[nold].name != NULL; nold++)
+ ;
+ else
+ nold = 0;
+ for (nnew = 0; new[nnew].name != NULL; nnew++)
+ ;
+ all = malloc(sizeof(struct ng_attribute) * (nold + nnew + 1));
+ memset(all,0,sizeof(struct ng_attribute) * (nold + nnew + 1));
+ memcpy(all,new,sizeof(struct ng_attribute)*nnew);
+ if (attrs) {
+ memcpy(all+nnew,attrs,sizeof(struct ng_attribute)*nold);
+ free(attrs);
+ }
+ attrs = all;
+
+#if 0
+ {
+ int i;
+ fprintf(stderr," <attr>\n");
+ for (i = 0; attrs[i].name != NULL; i++) {
+ fprintf(stderr," attr[%p]: %s \n",
+ attrs[i].handle,attrs[i].name);
+ }
+ fprintf(stderr," </attr>\n");
+ }
+#endif
+}
+
+/* ----------------------------------------------------------------------- */
+
+int
+do_va_cmd(int argc, ...)
+{
+ va_list ap;
+ int i;
+ char *argv[32];
+
+ va_start(ap,argc);
+ for (i = 0; i < argc; i++)
+ argv[i] = va_arg(ap,char*);
+ argv[i] = NULL;
+ va_end (ap);
+ return do_command(argc,argv);
+}
+
+int
+do_command(int argc, char **argv)
+{
+ int i;
+
+ if (argc == 0) {
+ fprintf(stderr,"do_command: no argument\n");
+ return -1;
+ }
+ if (debug) {
+ fprintf(stderr,"cmd:");
+ for (i = 0; i < argc; i++) {
+ fprintf(stderr," \"%s\"",argv[i]);
+ }
+ fprintf(stderr,"\n");
+ }
+
+ for (i = 0; commands[i].name != NULL; i++)
+ if (0 == strcasecmp(commands[i].name,argv[0]))
+ break;
+ if (commands[i].name == NULL) {
+ fprintf(stderr,"no handler for %s\n",argv[0]);
+ return -1;
+ }
+ if (argc-1 < commands[i].min_args) {
+ fprintf(stderr,"no enough args for %s\n",argv[0]);
+ return -1;
+ } else {
+ return commands[i].handler(argv[0],argc-1,argv+1);
+ }
+}
+
+char**
+split_cmdline(char *line, int *count)
+{
+ static char cmdline[1024];
+ static char *argv[32];
+ int argc,i;
+
+ strcpy(cmdline,line);
+ for (argc=0, i=0; argc<31;) {
+ argv[argc++] = cmdline+i;
+ while (cmdline[i] != ' ' &&
+ cmdline[i] != '\t' &&
+ cmdline[i] != '\0')
+ i++;
+ if (cmdline[i] == '\0')
+ break;
+ cmdline[i++] = '\0';
+ while (cmdline[i] == ' ' ||
+ cmdline[i] == '\t')
+ i++;
+ if (cmdline[i] == '\0')
+ break;
+ }
+ argv[argc] = NULL;
+
+ *count = argc;
+ return argv;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* sharing code does'nt work well for this one ... */
+static void
+set_capture(int capture, int tmp_switch)
+{
+ static int last_on = 0;
+
+ if (set_capture_hook) {
+ if (capture == CAPTURE_ON)
+ capture = last_on;
+
+ if (capture == CAPTURE_OVERLAY) {
+ /* can we do overlay ?? */
+ if (!(f_drv & CAN_OVERLAY))
+ capture = CAPTURE_GRABDISPLAY;
+ if (!do_overlay)
+ capture = CAPTURE_GRABDISPLAY;
+ }
+
+ if (cur_capture != capture) {
+ set_capture_hook(cur_capture,capture,tmp_switch);
+ cur_capture = capture;
+ }
+
+ if (cur_capture != CAPTURE_OFF)
+ last_on = cur_capture;
+ }
+}
+
+static void
+set_attr(struct ng_attribute *attr, int val)
+{
+ if (NULL == attr)
+ return;
+
+ attr->write(attr,val);
+ cur_attrs[attr->id] = val;
+ if (attr_notify)
+ attr_notify(attr,val);
+}
+
+static void
+set_volume(void)
+{
+ struct ng_attribute *attr;
+
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_VOLUME)))
+ attr->write(attr,cur_attrs[ATTR_ID_VOLUME]);
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_MUTE)))
+ attr->write(attr,cur_attrs[ATTR_ID_MUTE]);
+
+ if (volume_notify)
+ volume_notify();
+}
+
+static void
+set_freqtab(int j)
+{
+ chantab = j;
+ chanlist = chanlists[chantab].list;
+ chancount = chanlists[chantab].count;
+
+ /* cur_channel might be invalid (>chancount) right now */
+ cur_channel = -1;
+ /* this is valid for (struct CHANNEL*)->channel too */
+ calc_frequencies();
+
+ if (freqtab_notify)
+ freqtab_notify();
+}
+
+static void
+set_title(void)
+{
+ static char title[256];
+ const char *norm;
+
+ keypad_state = -1;
+ if (update_title) {
+ if (-1 != cur_sender) {
+ sprintf(title,"%s",channels[cur_sender]->name);
+ } else if (-1 != cur_channel) {
+ sprintf(title,"channel %s",chanlist[cur_channel].name);
+ if (cur_fine != 0)
+ sprintf(title+strlen(title)," (%d)",cur_fine);
+ norm = ng_attr_getstr(ng_attr_byid(attrs,ATTR_ID_NORM),
+ cur_attrs[ATTR_ID_NORM]);
+ sprintf(title+strlen(title)," (%s/%s)",
+ norm ? norm : "???", chanlists[chantab].name);
+ } else {
+ sprintf(title,"%.3f MHz",cur_freq/16.0);
+ }
+ update_title(title);
+ }
+}
+
+static void
+set_msg_int(struct ng_attribute *attr, int val)
+{
+ static char title[256];
+
+ if (display_message) {
+ sprintf(title,"%s: %d%%",attr->name,
+ ng_attr_int2percent(attr,val));
+ display_message(title);
+ }
+}
+
+static void
+set_msg_bool(const char *name, int val)
+{
+ static char title[256];
+
+ if (display_message) {
+ sprintf(title,"%s: %s",name, val ? "on" : "off");
+ display_message(title);
+ }
+}
+
+static void
+set_msg_str(char *name, char *val)
+{
+ static char title[256];
+
+ if (display_message) {
+ sprintf(title,"%s: %s",name,val);
+ display_message(title);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int update_int(struct ng_attribute *attr, int old, char *new)
+{
+ int value = old;
+ int step;
+
+ step = (attr->max - attr->min) * 3 / 100;
+ if (step == 0)
+ step = 1;
+
+ if (0 == strcasecmp(new,"inc"))
+ value += step;
+ else if (0 == strcasecmp(new,"dec"))
+ value -= step;
+ else if (0 == strncasecmp(new,"+=",2))
+ value += ng_attr_parse_int(attr,new+2);
+ else if (0 == strncasecmp(new,"-=",2))
+ value -= ng_attr_parse_int(attr,new+2);
+ else if (isdigit(new[0]) || '+' == new[0] || '-' == new[0])
+ value = ng_attr_parse_int(attr,new);
+ else
+ fprintf(stderr,"update_int: can't parse %s\n",new);
+
+ if (value < attr->min)
+ value = attr->min;
+ if (value > attr->max)
+ value = attr->max;
+ return value;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void
+attr_init()
+{
+ struct ng_attribute *attr;
+ int val;
+
+ for (attr = attrs; attr->name != NULL; attr++) {
+ if (attr->id == ATTR_ID_VOLUME ||
+ attr->id == ATTR_ID_MUTE)
+ continue;
+ val = attr->read(attr);
+ if (attr_notify)
+ attr_notify(attr,val);
+ cur_attrs[attr->id] = val;
+ }
+ if (-1 == defaults.color &&
+ NULL != ng_attr_byid(attrs,ATTR_ID_COLOR))
+ defaults.color = cur_attrs[ATTR_ID_COLOR];
+ if (-1 == defaults.bright &&
+ NULL != ng_attr_byid(attrs,ATTR_ID_BRIGHT))
+ defaults.bright = cur_attrs[ATTR_ID_BRIGHT];
+ if (-1 == defaults.hue &&
+ NULL != ng_attr_byid(attrs,ATTR_ID_HUE))
+ defaults.hue = cur_attrs[ATTR_ID_HUE];
+ if (-1 == defaults.contrast &&
+ NULL != ng_attr_byid(attrs,ATTR_ID_CONTRAST))
+ defaults.contrast = cur_attrs[ATTR_ID_CONTRAST];
+}
+
+void
+audio_init()
+{
+ struct ng_attribute *attr;
+
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_VOLUME)))
+ cur_attrs[ATTR_ID_VOLUME] = attr->read(attr);
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_MUTE)))
+ cur_attrs[ATTR_ID_MUTE] = attr->read(attr);
+ if (volume_notify)
+ volume_notify();
+}
+
+void
+audio_on()
+{
+ struct ng_attribute *attr,*list;
+
+ list = drv->list_attrs(h_drv);
+ attr = ng_attr_byid(list,ATTR_ID_MUTE);
+ if (NULL != attr)
+ attr->write(attr,0);
+}
+
+void
+audio_off()
+{
+ struct ng_attribute *attr,*list;
+
+ list = drv->list_attrs(h_drv);
+ attr = ng_attr_byid(list,ATTR_ID_MUTE);
+ if (NULL != attr)
+ attr->write(attr,1);
+}
+
+void
+set_defaults()
+{
+ struct ng_attribute *attr;
+
+ /* image parameters */
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_COLOR)))
+ set_attr(attr,defaults.color);
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_BRIGHT)))
+ set_attr(attr,defaults.bright);
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_HUE)))
+ set_attr(attr,defaults.hue);
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_CONTRAST)))
+ set_attr(attr,defaults.contrast);
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_INPUT)))
+ set_attr(attr,defaults.input);
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_NORM)))
+ set_attr(attr,defaults.norm);
+ set_capture(defaults.capture,0);
+
+ cur_channel = defaults.channel;
+ cur_fine = defaults.fine;
+ cur_freq = defaults.freq;
+ if (f_drv & CAN_TUNE)
+ drv->setfreq(h_drv,defaults.freq);
+}
+
+/* ----------------------------------------------------------------------- */
+
+#ifndef HAVE_STRCASESTR
+static char* strcasestr(char *haystack, char *needle)
+{
+ int hlen = strlen(haystack);
+ int nlen = strlen(needle);
+ int offset;
+
+ for (offset = 0; offset <= hlen - nlen; offset++)
+ if (0 == strncasecmp(haystack+offset,needle,nlen))
+ return haystack+offset;
+ return NULL;
+}
+#endif
+
+static int setstation_handler(char *name, int argc, char **argv)
+{
+ struct ng_attribute *attr,*mute;
+ int i;
+
+ if (0 == argc) {
+ set_title();
+ return 0;
+ }
+
+ if (cur_movie) {
+ if (display_message)
+ display_message("grabber busy");
+ return -1;
+ }
+
+ if (count && 0 == strcasecmp(argv[0],"next")) {
+ i = (cur_sender+1) % count;
+ } else if (count && 0 == strcasecmp(argv[0],"prev")) {
+ i = (cur_sender+count-1) % count;
+ } else if (count && 0 == strcasecmp(argv[0],"back")) {
+ if (-1 == last_sender)
+ return -1;
+ i = last_sender;
+ } else {
+ /* search the configured channels first... */
+ for (i = 0; i < count; i++)
+ if (0 == strcasecmp(channels[i]->name,argv[0]))
+ break;
+ /* ... next try substring matches ... */
+ if (i == count)
+ for (i = 0; i < count; i++)
+ if (NULL != strcasestr(channels[i]->name,argv[0]))
+ break;
+ /* ... next try using the argument as index ... */
+ if (i == count)
+ if (isdigit(argv[0][0]))
+ i = atoi(argv[0]);
+ if (i == count) {
+ /* ... sorry folks */
+ fprintf(stderr,"station \"%s\" not found\n",argv[0]);
+ return -1;
+ }
+ }
+
+ /* ok ?? */
+ if (i < 0 || i >= count)
+ return -1;
+
+ /* switch ... */
+ if (channel_switch_hook)
+ channel_switch_hook();
+ set_capture(CAPTURE_OFF,1);
+
+ last_sender = cur_sender;
+ cur_sender = i;
+
+ mute = ng_attr_byid(attrs,ATTR_ID_MUTE);
+ if (mute && !cur_attrs[ATTR_ID_MUTE])
+ mute->write(mute,1);
+
+ /* image parameters */
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_COLOR)))
+ set_attr(attr,channels[i]->color);
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_BRIGHT)))
+ set_attr(attr,channels[i]->bright);
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_HUE)))
+ set_attr(attr,channels[i]->hue);
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_CONTRAST)))
+ set_attr(attr,channels[i]->contrast);
+
+ /* input / norm */
+ if (cur_attrs[ATTR_ID_INPUT] != channels[i]->input)
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_INPUT)))
+ set_attr(attr,channels[i]->input);
+ if (cur_attrs[ATTR_ID_NORM] != channels[i]->norm)
+ if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_NORM)))
+ set_attr(attr,channels[i]->norm);
+
+ /* station */
+ cur_channel = channels[i]->channel;
+ cur_fine = channels[i]->fine;
+ cur_freq = channels[i]->freq;
+ if (f_drv & CAN_TUNE)
+ drv->setfreq(h_drv,channels[i]->freq);
+ set_capture(channels[i]->capture,0);
+
+ set_title();
+ if (setstation_notify)
+ setstation_notify();
+
+ if (mute && !cur_attrs[ATTR_ID_MUTE]) {
+ usleep(20000);
+ mute->write(mute,0);
+ }
+ return 0;
+}
+
+static int setchannel_handler(char *name, int argc, char **argv)
+{
+ struct ng_attribute *mute;
+ int c,i;
+
+ if (0 == argc) {
+ set_title();
+ return 0;
+ }
+
+ if (cur_movie) {
+ if (display_message)
+ display_message("grabber busy");
+ return -1;
+ }
+
+ if (0 == strcasecmp(argv[0],"next")) {
+ cur_channel = (cur_channel+1) % chancount;
+ cur_fine = defaults.fine;
+ } else if (0 == strcasecmp(argv[0],"prev")) {
+ cur_channel = (cur_channel+chancount-1) % chancount;
+ cur_fine = defaults.fine;
+ } else if (0 == strcasecmp(argv[0],"fine_up")) {
+ cur_fine++;
+ } else if (0 == strcasecmp(argv[0],"fine_down")) {
+ cur_fine--;
+ } else {
+ if (-1 != (c = lookup_channel(argv[0]))) {
+ cur_channel = c;
+ cur_fine = defaults.fine;
+ }
+ }
+
+ if (0 != strncmp(argv[0],"fine",4)) {
+ /* look if there is a known station on that channel */
+ for (i = 0; i < count; i++) {
+ if (cur_channel == channels[i]->channel) {
+ char *argv[2];
+ argv[0] = channels[i]->name;
+ argv[1] = NULL;
+ return setstation_handler("", argc, argv);
+ }
+ }
+ }
+
+ if (channel_switch_hook)
+ channel_switch_hook();
+ set_capture(CAPTURE_OFF,1);
+
+ cur_sender = -1;
+ if (-1 != cur_channel)
+ cur_freq = get_freq(cur_channel)+cur_fine;
+ else {
+ cur_freq += cur_fine;
+ cur_fine = 0;
+ }
+
+ mute = ng_attr_byid(attrs,ATTR_ID_MUTE);
+ if (mute && !cur_attrs[ATTR_ID_MUTE])
+ mute->write(mute,1);
+
+ if (f_drv & CAN_TUNE)
+ drv->setfreq(h_drv,cur_freq);
+ set_capture(defaults.capture,0);
+
+ set_title();
+ if (setstation_notify)
+ setstation_notify();
+
+ if (mute && !cur_attrs[ATTR_ID_MUTE]) {
+ usleep(20000);
+ mute->write(mute,0);
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void
+print_choices(char *name, char *value, struct STRTAB *tab)
+{
+ int i;
+
+ fprintf(stderr,"unknown %s: '%s' (available: ",name,value);
+ for (i = 0; tab[i].str != NULL; i++)
+ fprintf(stderr,"%s'%s'", (0 == i) ? "" : ", ", tab[i].str);
+ fprintf(stderr,")\n");
+}
+
+static int setfreqtab_handler(char *name, int argc, char **argv)
+{
+ int i;
+
+ i = str_to_int(argv[0],chanlist_names);
+ if (i != -1)
+ set_freqtab(i);
+ else
+ print_choices("freqtab",argv[0],chanlist_names);
+ return 0;
+}
+
+static int capture_handler(char *name, int argc, char **argv)
+{
+ int i;
+
+ if (0 == strcasecmp(argv[0],"toggle")) {
+ i = (cur_capture == CAPTURE_OFF) ? CAPTURE_ON : CAPTURE_OFF;
+ } else {
+ i = str_to_int(argv[0],captab);
+ }
+ if (i != -1)
+ set_capture(i,0);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int volume_handler(char *name, int argc, char **argv)
+{
+ struct ng_attribute *vol = ng_attr_byid(attrs,ATTR_ID_VOLUME);
+
+ if (0 == argc)
+ goto display;
+
+ if (0 == strcasecmp(argv[0],"mute")) {
+ /* mute on/off/toggle */
+ if (argc > 1) {
+ switch (str_to_int(argv[1],booltab)) {
+ case 0: cur_attrs[ATTR_ID_MUTE] = 0; break;
+ case 1: cur_attrs[ATTR_ID_MUTE] = 1; break;
+ default: cur_attrs[ATTR_ID_MUTE] = !cur_attrs[ATTR_ID_MUTE]; break;
+ }
+ } else {
+ cur_attrs[ATTR_ID_MUTE] = !cur_attrs[ATTR_ID_MUTE];
+ }
+ } else {
+ /* volume */
+ if (NULL != vol) {
+ cur_attrs[ATTR_ID_VOLUME] = vol->read(vol);
+ cur_attrs[ATTR_ID_VOLUME] =
+ update_int(vol,cur_attrs[ATTR_ID_VOLUME],argv[0]);
+ }
+ }
+ set_volume();
+
+ display:
+ if (cur_attrs[ATTR_ID_MUTE])
+ set_msg_str("volume","muted");
+ else {
+ if (vol)
+ set_msg_int(vol,cur_attrs[ATTR_ID_VOLUME]);
+ else
+ set_msg_str("volume","unmuted");
+ }
+ return 0;
+}
+
+static int attr_handler(char *name, int argc, char **argv)
+{
+ struct ng_attribute *attr;
+ int val,arg=0;
+
+ if (0 == strcasecmp(name,"setnorm")) {
+ attr = ng_attr_byname(attrs,"norm");
+
+ } else if (0 == strcasecmp(name,"setinput")) {
+ attr = ng_attr_byname(attrs,"input");
+
+ } else if (0 == strcasecmp(name,"setattr") &&
+ argc > 0) {
+ attr = ng_attr_byname(attrs,argv[arg++]);
+
+ } else {
+ attr = ng_attr_byname(attrs,name);
+ }
+
+ if (NULL == attr) {
+ fprintf(stderr,"cmd: %s: attribute not found\nvalid choices are:",
+ (arg > 0) ? argv[0] : name);
+ for (attr = attrs; attr->name != NULL; attr++)
+ fprintf(stderr,"%s \"%s\"",
+ (attr != attrs) ? "," : "", attr->name);
+ fprintf(stderr,"\n");
+ return -1;
+ }
+
+ if (!cur_movie && capture_get_hook)
+ capture_get_hook();
+ switch (attr->type) {
+ case ATTR_TYPE_CHOICE:
+ if (argc > arg) {
+ if (0 == strcasecmp("next", argv[arg])) {
+ val = cur_attrs[attr->id];
+ val++;
+ if (NULL == attr->choices[val].str)
+ val = 0;
+ } else {
+ val = ng_attr_getint(attr, argv[arg]);
+ }
+ if (-1 == val) {
+ fprintf(stderr,"invalid value for %s: %s\n",attr->name,argv[arg]);
+ ng_attr_listchoices(attr);
+ } else {
+ set_attr(attr,val);
+ }
+ }
+ break;
+ case ATTR_TYPE_INTEGER:
+ if (argc > arg) {
+ cur_attrs[attr->id] = attr->read(attr);
+ val = update_int(attr,cur_attrs[attr->id],argv[arg]);
+ set_attr(attr,val);
+ }
+ set_msg_int(attr,cur_attrs[attr->id]);
+ break;
+ case ATTR_TYPE_BOOL:
+ if (argc > arg) {
+ val = str_to_int(argv[arg],booltab);
+ if (-1 == val) {
+ if (0 == strcasecmp(argv[arg],"toggle"))
+ val = !cur_attrs[attr->id];
+ }
+ set_attr(attr,val);
+ }
+ set_msg_bool(attr->name,cur_attrs[attr->id]);
+ break;
+ }
+ if (!cur_movie && capture_rel_hook)
+ capture_rel_hook();
+ return 0;
+}
+
+static int show_handler(char *name, int argc, char **argv)
+{
+ struct ng_attribute *attr;
+ char *n[2] = { NULL, NULL };
+ int val;
+
+ if (0 == argc) {
+ for (attr = attrs; attr->name != NULL; attr++) {
+ n[0] = (char*)attr->name;
+ show_handler("show", 1, n);
+ }
+ return 0;
+ }
+
+ attr = ng_attr_byname(attrs,argv[0]);
+ if (NULL == attr) {
+ fprintf(stderr,"fixme: 404 %s\n",argv[0]);
+ return 0;
+ }
+ val = cur_attrs[attr->id];
+ switch (attr->type) {
+ case ATTR_TYPE_CHOICE:
+ printf("%s: %s\n", attr->name, ng_attr_getstr(attr,val));
+ break;
+ case ATTR_TYPE_INTEGER:
+ printf("%s: %d\n", attr->name, val);
+ break;
+ case ATTR_TYPE_BOOL:
+ printf("%s: %s\n", attr->name, val ? "on" : "off");
+ break;
+ }
+ return 0;
+}
+
+static int list_handler(char *name, int argc, char **argv)
+{
+ struct ng_attribute *attr;
+ int val,i;
+
+ printf("%-10.10s | type | %-7.7s | %-7.7s | %s\n",
+ "attribute","current","default","comment");
+ printf("-----------+--------+---------+--------"
+ "-+-------------------------------------\n");
+ for (attr = attrs; attr->name != NULL; attr++) {
+ val = cur_attrs[attr->id];
+ switch (attr->type) {
+ case ATTR_TYPE_CHOICE:
+ printf("%-10.10s | choice | %-7.7s | %-7.7s |",
+ attr->name,
+ ng_attr_getstr(attr,val),
+ ng_attr_getstr(attr,attr->defval));
+ for (i = 0; attr->choices[i].str != NULL; i++)
+ printf(" %s",attr->choices[i].str);
+ printf("\n");
+ break;
+ case ATTR_TYPE_INTEGER:
+ printf("%-10.10s | int | %7d | %7d | range is %d => %d\n",
+ attr->name, val, attr->defval,
+ attr->min, attr->max);
+ break;
+ case ATTR_TYPE_BOOL:
+ printf("%-10.10s | bool | %-7.7s | %-7.7s |\n",
+ attr->name,
+ val ? "on" : "off",
+ attr->defval ? "on" : "off");
+ break;
+ }
+ }
+ return 0;
+}
+
+static int dattr_handler(char *name, int argc, char **argv)
+{
+ struct ng_attribute *attr = NULL;
+ int i;
+
+ if (argc > 0 && 0 == strcasecmp(argv[0],"next")) {
+ for (i = 0; i < NUM_DATTR; i++) {
+ cur_dattr++;
+ cur_dattr %= NUM_DATTR;
+ attr = ng_attr_byid(attrs,dattr[cur_dattr]);
+ if (NULL != attr)
+ break;
+ }
+ if (NULL == attr)
+ return 0;
+ argc = 0;
+ }
+ if (NULL == attr)
+ attr = ng_attr_byid(attrs,dattr[cur_dattr]);
+ if (NULL == attr)
+ return 0;
+ return attr_handler((char*)attr->name,argc,argv);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int snap_handler(char *hname, int argc, char **argv)
+{
+ char message[512];
+ char *tmpfilename = NULL;
+ char *filename = NULL;
+ char *name;
+ int jpeg = 0;
+ int ret = 0;
+ struct ng_video_fmt fmt;
+ struct ng_video_buf *buf = NULL;
+
+ if (!(f_drv & CAN_CAPTURE)) {
+ fprintf(stderr,"grabbing: not supported [try -noxv switch?]\n");
+ return -1;
+ }
+
+ if (cur_movie) {
+ if (display_message)
+ display_message("grabber busy");
+ return -1;
+ }
+
+ if (capture_get_hook)
+ capture_get_hook();
+
+ /* format */
+ if (argc > 0) {
+ if (0 == strcasecmp(argv[0],"jpeg"))
+ jpeg = 1;
+ if (0 == strcasecmp(argv[0],"ppm"))
+ jpeg = 0;
+ }
+
+ /* size */
+ memset(&fmt,0,sizeof(fmt));
+ fmt.fmtid = VIDEO_RGB24;
+ fmt.width = 2048;
+ fmt.height = 1572;
+ if (argc > 1) {
+ if (0 == strcasecmp(argv[1],"full")) {
+ /* nothing */
+ } else if (0 == strcasecmp(argv[1],"win")) {
+ fmt.width = x11_fmt.width;
+ fmt.height = x11_fmt.height;
+ } else if (2 == sscanf(argv[1],"%dx%d",&fmt.width,&fmt.height)) {
+ /* nothing */
+ } else {
+ return -1;
+ }
+ }
+
+ /* filename */
+ if (argc > 2)
+ filename = argv[2];
+
+ if (NULL == (buf = ng_grabber_get_image(&fmt))) {
+ if (display_message)
+ display_message("grabbing failed");
+ ret = -1;
+ goto done;
+ }
+ buf = ng_filter_single(cur_filter,buf);
+
+ if (NULL == filename) {
+ if (-1 != cur_sender) {
+ name = channels[cur_sender]->name;
+ } else if (-1 != cur_channel) {
+ name = chanlist[cur_channel].name;
+ } else {
+ name = "unknown";
+ }
+ filename = snap_filename(snapbase, name, jpeg ? "jpeg" : "ppm");
+ }
+ tmpfilename = malloc(strlen(filename)+8);
+ sprintf(tmpfilename,"%s.$$$",filename);
+
+ if (jpeg) {
+ if (-1 == write_jpeg(tmpfilename, buf, ng_jpeg_quality, 0)) {
+ sprintf(message,"open %s: %s\n",tmpfilename,strerror(errno));
+ } else {
+ sprintf(message,"saved jpeg: %s",filename);
+ }
+ } else {
+ if (-1 == write_ppm(tmpfilename, buf)) {
+ sprintf(message,"open %s: %s\n",tmpfilename,strerror(errno));
+ } else {
+ sprintf(message,"saved ppm: %s",filename);
+ }
+ }
+ unlink(filename);
+ if (-1 == link(tmpfilename,filename)) {
+ fprintf(stderr,"link(%s,%s): %s\n",
+ tmpfilename,filename,strerror(errno));
+ goto done;
+ }
+ unlink(tmpfilename);
+ if (display_message)
+ display_message(message);
+
+done:
+ if (tmpfilename)
+ free(tmpfilename);
+ if (NULL != buf)
+ ng_release_video_buf(buf);
+ if (capture_rel_hook)
+ capture_rel_hook();
+ return ret;
+}
+
+static int webcam_handler(char *hname, int argc, char **argv)
+{
+ struct ng_video_fmt fmt;
+ struct ng_video_buf *buf;
+
+ if (webcam)
+ free(webcam);
+ webcam = strdup(argv[0]);
+
+ /* if either avi recording or grabdisplay is active, we do
+ /not/ stop capture and switch the video format. The next
+ capture will send a copy of the frame to the webcam thread
+ and it has to deal with it as-is */
+ if (cur_movie)
+ return 0;
+ if (cur_capture == CAPTURE_GRABDISPLAY)
+ return 0;
+
+ /* if no capture is running we can switch to RGB first to make
+ the webcam happy */
+ if (capture_get_hook)
+ capture_get_hook();
+ fmt = x11_fmt;
+ fmt.fmtid = VIDEO_RGB24;
+ buf = ng_grabber_get_image(&fmt);
+ if (buf)
+ ng_release_video_buf(buf);
+ if (capture_rel_hook)
+ capture_rel_hook();
+ return 0;
+}
+
+static int movie_handler(char *name, int argc, char **argv)
+{
+ if (!movie_hook)
+ return 0;
+ movie_hook(argc,argv);
+ return 0;
+}
+
+static int
+fullscreen_handler(char *name, int argc, char **argv)
+{
+ if (fullscreen_hook)
+ fullscreen_hook();
+ return 0;
+}
+
+static int
+msg_handler(char *name, int argc, char **argv)
+{
+ if (display_message)
+ display_message(argv[0]);
+ return 0;
+}
+
+static int
+showtime_handler(char *name, int argc, char **argv)
+{
+ char timestr[6];
+ struct tm *times;
+ time_t timet;
+
+ timet = time(NULL);
+ times = localtime(&timet);
+ strftime(timestr, 6, "%k:%M", times);
+ if (display_message)
+ display_message(timestr);
+ return 0;
+}
+
+static int
+exit_handler(char *name, int argc, char **argv)
+{
+ if (exit_hook)
+ exit_hook();
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct TEXTELEM*
+parse_vtx(int lines, char **text)
+{
+ static char *names[8] = { "black", "red", "green", "yellow",
+ "blue", "magenta", "cyan", "white" };
+ static struct TEXTELEM tt[VTX_COUNT];
+ int i,n,t,ansi;
+ char *ansi_fg,*ansi_bg;
+
+ /* parse */
+ t = 0;
+ memset(tt,0,sizeof(tt));
+ for (i = 0; i < lines; i++) {
+ tt[t].line = i;
+ ansi_fg = NULL; ansi_bg = NULL;
+ for (n = 0; text[i][n] != 0;) {
+ if (text[i][n] == '\033') {
+ if (tt[t].len) {
+ t++;
+ if (VTX_COUNT == t)
+ return tt;
+ }
+ n++;
+ if (text[i][n] == '[') {
+ /* ANSI color tty sequences */
+ n++;
+ for (ansi=1;ansi;) {
+ switch (text[i][n]) {
+ case '3':
+ n++;
+ if (text[i][n] >= '0' && text[i][n] < '8') {
+ ansi_fg = names[text[i][n]-'0'];
+ n++;
+ }
+ break;
+ case '4':
+ n++;
+ if (text[i][n] >= '0' && text[i][n] < '8') {
+ ansi_bg = names[text[i][n]-'0'];
+ n++;
+ }
+ break;
+ case '1':
+ case ';':
+ n++;
+ break;
+ case 'm':
+ n++;
+ /* ok, commit */
+ ansi=0;
+ tt[t].fg = ansi_fg;
+ tt[t].bg = ansi_bg;
+ break;
+ default:
+ /* error */
+ ansi=0;
+ }
+ }
+ } else {
+ /* old way: ESC fg bg */
+ if (text[i][n] >= '0' && text[i][n] < '8') {
+ tt[t].fg = names[text[i][n]-'0'];
+ n++;
+ }
+ if (text[i][n] >= '0' && text[i][n] < '8') {
+ tt[t].bg = names[text[i][n]-'0'];
+ n++;
+ }
+ }
+ tt[t].line = i;
+ } else {
+ tt[t].str[tt[t].len++] = text[i][n];
+ n++;
+ if (tt[t].len >= VTX_LEN-1) {
+ t++;
+ if (VTX_COUNT == t)
+ return tt;
+ tt[t].line = i;
+ }
+ }
+ }
+ if (tt[t].len) {
+ t++;
+ if (VTX_COUNT == t)
+ break;
+ }
+ }
+ return tt;
+}
+
+static int
+vtx_handler(char *name, int argc, char **argv)
+{
+ struct TEXTELEM *tt;
+
+ if (vtx_message) {
+ if (argc) {
+ tt = parse_vtx(argc,argv);
+ vtx_message(tt);
+ } else {
+ vtx_message(NULL);
+ }
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+#define CH_MAX (keypad_ntsc ? 99 : count)
+
+static int
+keypad_handler(char *name, int argc, char **argv)
+{
+ int n = atoi(argv[0])%10;
+ char msg[8],ch[8];
+
+ if (debug)
+ fprintf(stderr,"keypad: key %d\n",n);
+ if (-1 == keypad_state) {
+ if ((keypad_partial && n > 0 && n <= CH_MAX) ||
+ (!keypad_partial && n > 0 && n <= CH_MAX && 10*n > CH_MAX)) {
+ if (keypad_ntsc) {
+ sprintf(ch,"%d",n);
+ do_va_cmd(2,"setchannel",ch,NULL);
+ } else
+ do_va_cmd(2,"setstation",channels[n-1]->name,NULL);
+ }
+ if (n >= 0 && 10*n <= CH_MAX) {
+ if (debug)
+ fprintf(stderr,"keypad: hang: %d\n",n);
+ keypad_state = n;
+ if (display_message) {
+ sprintf(msg,"%d_",n);
+ display_message(msg);
+ }
+ }
+ } else {
+ if ((n+keypad_state*10) <= CH_MAX)
+ n += keypad_state*10;
+ keypad_state = -1;
+ if (debug)
+ fprintf(stderr,"keypad: ok: %d\n",n);
+ if (n > 0 && n <= CH_MAX) {
+ if (keypad_ntsc) {
+ sprintf(ch,"%d",n);
+ do_va_cmd(2,"setchannel",ch,NULL);
+ } else
+ do_va_cmd(2,"setstation",channels[n-1]->name,NULL);
+ }
+ }
+ return 0;
+}
+
+void
+keypad_timeout(void)
+{
+ if (debug)
+ fprintf(stderr,"keypad: timeout\n");
+ if (keypad_state == cur_sender+1)
+ set_title();
+ keypad_state = -1;
+}
diff --git a/common/commands.h b/common/commands.h
new file mode 100644
index 0000000..20b4229
--- /dev/null
+++ b/common/commands.h
@@ -0,0 +1,69 @@
+
+#define VTX_COUNT 256
+#define VTX_LEN 64
+
+struct TEXTELEM {
+ char str[VTX_LEN];
+ char *fg;
+ char *bg;
+ int len;
+ int line;
+ int x,y;
+};
+
+/*------------------------------------------------------------------------*/
+
+/* feedback for the user */
+extern void (*update_title)(char *message);
+extern void (*display_message)(char *message);
+extern void (*vtx_message)(struct TEXTELEM *tt);
+extern void (*rec_status)(char *message);
+
+/* for updating GUI elements / whatever */
+extern void (*attr_notify)(struct ng_attribute *attr, int val);
+extern void (*volume_notify)(void);
+extern void (*freqtab_notify)(void);
+extern void (*setfreqtab_notify)(void);
+extern void (*setstation_notify)(void);
+
+/* gets called _before_ channel switches */
+extern void (*channel_switch_hook)(void);
+
+/* capture overlay/grab/off */
+extern void (*set_capture_hook)(int old, int new, int tmp_switch);
+
+/* toggle fullscreen */
+extern void (*fullscreen_hook)(void);
+extern void (*exit_hook)(void);
+extern void (*capture_get_hook)(void);
+extern void (*capture_rel_hook)(void);
+extern void (*movie_hook)(int argc, char **argv);
+
+extern int debug;
+extern int do_overlay;
+extern char *snapbase;
+extern int have_shmem;
+extern struct ng_video_fmt x11_fmt;
+extern int cur_movie,cur_attrs[256];
+extern struct movie_parm m_parm;
+
+extern const struct ng_vid_driver *drv;
+extern void *h_drv;
+extern int f_drv;
+
+extern struct ng_attribute *attrs;
+
+/*------------------------------------------------------------------------*/
+
+void attr_init(void);
+void audio_init(void);
+void audio_on(void);
+void audio_off(void);
+void set_defaults(void);
+
+void add_attrs(struct ng_attribute *new);
+
+int do_va_cmd(int argc, ...);
+int do_command(int argc, char **argv);
+char** split_cmdline(char *line, int *count);
+void keypad_timeout(void);
diff --git a/common/event.c b/common/event.c
new file mode 100644
index 0000000..cdf584c
--- /dev/null
+++ b/common/event.c
@@ -0,0 +1,157 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "grab-ng.h"
+#include "commands.h"
+#include "event.h"
+#include "parseconfig.h"
+
+/* ----------------------------------------------------------------------- */
+
+static struct event_entry *event_conf_list;
+static struct event_entry *event_builtin_list;
+
+/* ----------------------------------------------------------------------- */
+
+static void parse_action(struct event_entry *entry)
+{
+ char *token,*h;
+
+ strcpy(entry->argbuf,entry->action);
+ h = entry->argbuf;
+ for (;;) {
+ while (' ' == *h || '\t' == *h)
+ h++;
+ if ('\0' == *h)
+ break;
+ if ('"' == *h) {
+ /* quoted string */
+ h++;
+ token = h;
+ while ('\0' != *h && '"' != *h)
+ h++;
+ } else {
+ /* normal string */
+ token = h;
+ while ('\0' != *h && ' ' != *h && '\t' != *h)
+ h++;
+ }
+ if ('\0' != *h) {
+ *h = 0;
+ h++;
+ }
+ entry->argv[entry->argc++] = token;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+int event_register(char *event, char *action)
+{
+ struct event_entry *entry;
+
+ entry = malloc(sizeof(*entry));
+ memset(entry,0,sizeof(*entry));
+ strncpy(entry->event,event,127);
+ strncpy(entry->action,action,127);
+ entry->next = event_conf_list;
+ event_conf_list = entry;
+ parse_action(entry);
+ if (debug)
+ fprintf(stderr,"ev: reg conf \"%s\" => \"%s\"\n",
+ entry->event,entry->action);
+ return 0;
+}
+
+int event_register_list(struct event_entry *entry)
+{
+ for (; NULL != entry && 0 != entry->event[0]; entry++) {
+ entry->next = event_builtin_list;
+ event_builtin_list = entry;
+ parse_action(entry);
+ if (debug)
+ fprintf(stderr,"ev: reg built-in \"%s\" => \"%s\"\n",
+ entry->event,entry->action);
+ }
+ return 0;
+}
+
+void event_readconfig(void)
+{
+ char **list,*val;
+
+ list = cfg_list_entries("eventmap");
+ if (NULL == list)
+ return;
+
+ for (; *list != NULL; list++)
+ if (NULL != (val = cfg_get_str("eventmap",*list)))
+ event_register(*list,val);
+}
+
+void event_writeconfig(FILE *fp)
+{
+ struct event_entry *entry;
+
+ if (NULL == event_conf_list)
+ return;
+
+ fprintf(fp,"[eventmap]\n");
+ for (entry = event_conf_list; NULL != entry; entry = entry->next)
+ fprintf(fp,"%s = %s\n",entry->event,entry->action);
+ fprintf(fp,"\n");
+}
+
+/* ----------------------------------------------------------------------- */
+
+int event_dispatch(char *event)
+{
+ struct event_entry *entry = NULL;
+ char *name,*arg,*h,*argv[EVENT_ARGV_SIZE];
+ int argc;
+
+ /* parse */
+ if (NULL != (h = strchr(event,'('))) {
+ name = event;
+ arg = h+1;
+ *h = 0;
+ if (NULL != (h = strchr(arg,')')))
+ *h = 0;
+ if (debug)
+ fprintf(stderr,"ev: dispatch name=%s arg=%s\n",name,arg);
+ } else {
+ name = event;
+ arg = NULL;
+ if (debug)
+ fprintf(stderr,"ev: dispatch name=%s\n",name);
+ }
+
+ /* search lists */
+ if (NULL == entry)
+ for (entry = event_conf_list; NULL != entry; entry = entry->next)
+ if (0 == strcasecmp(name,entry->event))
+ break;
+ if (NULL == entry)
+ for (entry = event_builtin_list; NULL != entry; entry = entry->next)
+ if (0 == strcasecmp(name,entry->event))
+ break;
+ if (NULL == entry) {
+ if (debug)
+ fprintf(stderr,"ev: 404: %s\n",name);
+ return 0;
+ }
+
+ /* call action */
+ memcpy(argv,entry->argv,sizeof(argv));
+ argc = entry->argc;
+ if (arg)
+ argv[argc++] = arg;
+ do_command(argc,argv);
+
+ return 0;
+}
diff --git a/common/event.h b/common/event.h
new file mode 100644
index 0000000..8f14e57
--- /dev/null
+++ b/common/event.h
@@ -0,0 +1,23 @@
+#define EVENT_ARGV_SIZE 16
+
+struct event_entry {
+ /* the entry */
+ char event[128];
+ char action[128];
+
+ /* pre-parsed action for do_command */
+ char argbuf[128];
+ int argc;
+ char *argv[EVENT_ARGV_SIZE];
+
+ /* linked list */
+ struct event_entry *next;
+};
+
+int event_register(char *event, char *action);
+int event_register_list(struct event_entry *entry);
+
+void event_readconfig(void);
+void event_writeconfig(FILE *fp);
+
+int event_dispatch(char *event);
diff --git a/common/frequencies.c b/common/frequencies.c
new file mode 100644
index 0000000..4863736
--- /dev/null
+++ b/common/frequencies.c
@@ -0,0 +1,1250 @@
+#include <stdlib.h>
+#include <sys/time.h>
+#include <pthread.h>
+
+#include "frequencies.h"
+#include "grab-ng.h"
+
+/* --------------------------------------------------------------------- */
+
+/* US broadcast */
+static struct CHANLIST ntsc_bcast[] = {
+ { "2", 55250 },
+ { "3", 61250 },
+ { "4", 67250 },
+ { "5", 77250 },
+ { "6", 83250 },
+ { "7", 175250 },
+ { "8", 181250 },
+ { "9", 187250 },
+ { "10", 193250 },
+ { "11", 199250 },
+ { "12", 205250 },
+ { "13", 211250 },
+ { "14", 471250 },
+ { "15", 477250 },
+ { "16", 483250 },
+ { "17", 489250 },
+ { "18", 495250 },
+ { "19", 501250 },
+ { "20", 507250 },
+ { "21", 513250 },
+ { "22", 519250 },
+ { "23", 525250 },
+ { "24", 531250 },
+ { "25", 537250 },
+ { "26", 543250 },
+ { "27", 549250 },
+ { "28", 555250 },
+ { "29", 561250 },
+ { "30", 567250 },
+ { "31", 573250 },
+ { "32", 579250 },
+ { "33", 585250 },
+ { "34", 591250 },
+ { "35", 597250 },
+ { "36", 603250 },
+ { "37", 609250 },
+ { "38", 615250 },
+ { "39", 621250 },
+ { "40", 627250 },
+ { "41", 633250 },
+ { "42", 639250 },
+ { "43", 645250 },
+ { "44", 651250 },
+ { "45", 657250 },
+ { "46", 663250 },
+ { "47", 669250 },
+ { "48", 675250 },
+ { "49", 681250 },
+ { "50", 687250 },
+ { "51", 693250 },
+ { "52", 699250 },
+ { "53", 705250 },
+ { "54", 711250 },
+ { "55", 717250 },
+ { "56", 723250 },
+ { "57", 729250 },
+ { "58", 735250 },
+ { "59", 741250 },
+ { "60", 747250 },
+ { "61", 753250 },
+ { "62", 759250 },
+ { "63", 765250 },
+ { "64", 771250 },
+ { "65", 777250 },
+ { "66", 783250 },
+ { "67", 789250 },
+ { "68", 795250 },
+ { "69", 801250 },
+
+ { "70", 807250 },
+ { "71", 813250 },
+ { "72", 819250 },
+ { "73", 825250 },
+ { "74", 831250 },
+ { "75", 837250 },
+ { "76", 843250 },
+ { "77", 849250 },
+ { "78", 855250 },
+ { "79", 861250 },
+ { "80", 867250 },
+ { "81", 873250 },
+ { "82", 879250 },
+ { "83", 885250 },
+};
+
+/* US cable */
+static struct CHANLIST ntsc_cable[] = {
+ { "1", 73250 },
+ { "2", 55250 },
+ { "3", 61250 },
+ { "4", 67250 },
+ { "5", 77250 },
+ { "6", 83250 },
+ { "7", 175250 },
+ { "8", 181250 },
+ { "9", 187250 },
+ { "10", 193250 },
+ { "11", 199250 },
+ { "12", 205250 },
+
+ { "13", 211250 },
+ { "14", 121250 },
+ { "15", 127250 },
+ { "16", 133250 },
+ { "17", 139250 },
+ { "18", 145250 },
+ { "19", 151250 },
+ { "20", 157250 },
+
+ { "21", 163250 },
+ { "22", 169250 },
+ { "23", 217250 },
+ { "24", 223250 },
+ { "25", 229250 },
+ { "26", 235250 },
+ { "27", 241250 },
+ { "28", 247250 },
+ { "29", 253250 },
+ { "30", 259250 },
+ { "31", 265250 },
+ { "32", 271250 },
+ { "33", 277250 },
+ { "34", 283250 },
+ { "35", 289250 },
+ { "36", 295250 },
+ { "37", 301250 },
+ { "38", 307250 },
+ { "39", 313250 },
+ { "40", 319250 },
+ { "41", 325250 },
+ { "42", 331250 },
+ { "43", 337250 },
+ { "44", 343250 },
+ { "45", 349250 },
+ { "46", 355250 },
+ { "47", 361250 },
+ { "48", 367250 },
+ { "49", 373250 },
+ { "50", 379250 },
+ { "51", 385250 },
+ { "52", 391250 },
+ { "53", 397250 },
+ { "54", 403250 },
+ { "55", 409250 },
+ { "56", 415250 },
+ { "57", 421250 },
+ { "58", 427250 },
+ { "59", 433250 },
+ { "60", 439250 },
+ { "61", 445250 },
+ { "62", 451250 },
+ { "63", 457250 },
+ { "64", 463250 },
+ { "65", 469250 },
+ { "66", 475250 },
+ { "67", 481250 },
+ { "68", 487250 },
+ { "69", 493250 },
+
+ { "70", 499250 },
+ { "71", 505250 },
+ { "72", 511250 },
+ { "73", 517250 },
+ { "74", 523250 },
+ { "75", 529250 },
+ { "76", 535250 },
+ { "77", 541250 },
+ { "78", 547250 },
+ { "79", 553250 },
+ { "80", 559250 },
+ { "81", 565250 },
+ { "82", 571250 },
+ { "83", 577250 },
+ { "84", 583250 },
+ { "85", 589250 },
+ { "86", 595250 },
+ { "87", 601250 },
+ { "88", 607250 },
+ { "89", 613250 },
+ { "90", 619250 },
+ { "91", 625250 },
+ { "92", 631250 },
+ { "93", 637250 },
+ { "94", 643250 },
+ { "95", 91250 },
+ { "96", 97250 },
+ { "97", 103250 },
+ { "98", 109250 },
+ { "99", 115250 },
+ { "100", 649250 },
+ { "101", 655250 },
+ { "102", 661250 },
+ { "103", 667250 },
+ { "104", 673250 },
+ { "105", 679250 },
+ { "106", 685250 },
+ { "107", 691250 },
+ { "108", 697250 },
+ { "109", 703250 },
+ { "110", 709250 },
+ { "111", 715250 },
+ { "112", 721250 },
+ { "113", 727250 },
+ { "114", 733250 },
+ { "115", 739250 },
+ { "116", 745250 },
+ { "117", 751250 },
+ { "118", 757250 },
+ { "119", 763250 },
+ { "120", 769250 },
+ { "121", 775250 },
+ { "122", 781250 },
+ { "123", 787250 },
+ { "124", 793250 },
+ { "125", 799250 },
+
+ { "T7", 8250 },
+ { "T8", 14250 },
+ { "T9", 20250 },
+ { "T10", 26250 },
+ { "T11", 32250 },
+ { "T12", 38250 },
+ { "T13", 44250 },
+ { "T14", 50250 }
+};
+
+/* US HRC */
+static struct CHANLIST ntsc_hrc[] = {
+ { "1", 72000 },
+ { "2", 54000 },
+ { "3", 60000 },
+ { "4", 66000 },
+ { "5", 78000 },
+ { "6", 84000 },
+ { "7", 174000 },
+ { "8", 180000 },
+ { "9", 186000 },
+ { "10", 192000 },
+ { "11", 198000 },
+ { "12", 204000 },
+
+ { "13", 210000 },
+ { "14", 120000 },
+ { "15", 126000 },
+ { "16", 132000 },
+ { "17", 138000 },
+ { "18", 144000 },
+ { "19", 150000 },
+ { "20", 156000 },
+
+ { "21", 162000 },
+ { "22", 168000 },
+ { "23", 216000 },
+ { "24", 222000 },
+ { "25", 228000 },
+ { "26", 234000 },
+ { "27", 240000 },
+ { "28", 246000 },
+ { "29", 252000 },
+ { "30", 258000 },
+ { "31", 264000 },
+ { "32", 270000 },
+ { "33", 276000 },
+ { "34", 282000 },
+ { "35", 288000 },
+ { "36", 294000 },
+ { "37", 300000 },
+ { "38", 306000 },
+ { "39", 312000 },
+ { "40", 318000 },
+ { "41", 324000 },
+ { "42", 330000 },
+ { "43", 336000 },
+ { "44", 342000 },
+ { "45", 348000 },
+ { "46", 354000 },
+ { "47", 360000 },
+ { "48", 366000 },
+ { "49", 372000 },
+ { "50", 378000 },
+ { "51", 384000 },
+ { "52", 390000 },
+ { "53", 396000 },
+ { "54", 402000 },
+ { "55", 408000 },
+ { "56", 414000 },
+ { "57", 420000 },
+ { "58", 426000 },
+ { "59", 432000 },
+ { "60", 438000 },
+ { "61", 444000 },
+ { "62", 450000 },
+ { "63", 456000 },
+ { "64", 462000 },
+ { "65", 468000 },
+ { "66", 474000 },
+ { "67", 480000 },
+ { "68", 486000 },
+ { "69", 492000 },
+
+ { "70", 498000 },
+ { "71", 504000 },
+ { "72", 510000 },
+ { "73", 516000 },
+ { "74", 522000 },
+ { "75", 528000 },
+ { "76", 534000 },
+ { "77", 540000 },
+ { "78", 546000 },
+ { "79", 552000 },
+ { "80", 558000 },
+ { "81", 564000 },
+ { "82", 570000 },
+ { "83", 576000 },
+ { "84", 582000 },
+ { "85", 588000 },
+ { "86", 594000 },
+ { "87", 600000 },
+ { "88", 606000 },
+ { "89", 612000 },
+ { "90", 618000 },
+ { "91", 624000 },
+ { "92", 630000 },
+ { "93", 636000 },
+ { "94", 642000 },
+ { "95", 900000 },
+ { "96", 960000 },
+ { "97", 102000 },
+ { "98", 108000 },
+ { "99", 114000 },
+ { "100", 648000 },
+ { "101", 654000 },
+ { "102", 660000 },
+ { "103", 666000 },
+ { "104", 672000 },
+ { "105", 678000 },
+ { "106", 684000 },
+ { "107", 690000 },
+ { "108", 696000 },
+ { "109", 702000 },
+ { "110", 708000 },
+ { "111", 714000 },
+ { "112", 720000 },
+ { "113", 726000 },
+ { "114", 732000 },
+ { "115", 738000 },
+ { "116", 744000 },
+ { "117", 750000 },
+ { "118", 756000 },
+ { "119", 762000 },
+ { "120", 768000 },
+ { "121", 774000 },
+ { "122", 780000 },
+ { "123", 786000 },
+ { "124", 792000 },
+ { "125", 798000 },
+
+ { "T7", 7000 },
+ { "T8", 13000 },
+ { "T9", 19000 },
+ { "T10", 25000 },
+ { "T11", 31000 },
+ { "T12", 37000 },
+ { "T13", 43000 },
+ { "T14", 49000 },
+};
+
+/* --------------------------------------------------------------------- */
+
+/* Canada cable */
+static struct CHANLIST ntsc_cable_ca[] = {
+ { "2", 61750 },
+ { "3", 67750 },
+ { "4", 73750 },
+ { "5", 83750 },
+ { "6", 89750 },
+ { "7", 181750 },
+ { "8", 187750 },
+ { "9", 193750 },
+ { "10", 199750 },
+ { "11", 205750 },
+ { "12", 211750 },
+
+ { "13", 217750 },
+ { "14", 127750 },
+ { "15", 133750 },
+ { "16", 139750 },
+ { "17", 145750 },
+ { "18", 151750 },
+ { "19", 157750 },
+ { "20", 163750 },
+
+ { "21", 169750 },
+ { "22", 175750 },
+ { "23", 223750 },
+ { "24", 229750 },
+ { "25", 235750 },
+ { "26", 241750 },
+ { "27", 247750 },
+ { "28", 253750 },
+ { "29", 259750 },
+ { "30", 265750 },
+ { "31", 271750 },
+ { "32", 277750 },
+ { "33", 283750 },
+ { "34", 289750 },
+ { "35", 295750 },
+ { "36", 301750 },
+ { "37", 307750 },
+ { "38", 313750 },
+ { "39", 319750 },
+ { "40", 325750 },
+ { "41", 331750 },
+ { "42", 337750 },
+ { "43", 343750 },
+ { "44", 349750 },
+ { "45", 355750 },
+ { "46", 361750 },
+ { "47", 367750 },
+ { "48", 373750 },
+ { "49", 379750 },
+ { "50", 385750 },
+ { "51", 391750 },
+ { "52", 397750 },
+ { "53", 403750 },
+ { "54", 409750 },
+ { "55", 415750 },
+ { "56", 421750 },
+ { "57", 427750 },
+ { "58", 433750 },
+ { "59", 439750 },
+ { "60", 445750 },
+ { "61", 451750 },
+ { "62", 457750 },
+ { "63", 463750 },
+ { "64", 469750 },
+ { "65", 475750 },
+ { "66", 481750 },
+ { "67", 487750 },
+ { "68", 493750 },
+ { "69", 499750 },
+
+ { "70", 505750 },
+ { "71", 511750 },
+ { "72", 517750 },
+ { "73", 523750 },
+ { "74", 529750 },
+ { "75", 535750 },
+ { "76", 541750 },
+ { "77", 547750 },
+ { "78", 553750 },
+ { "79", 559750 },
+ { "80", 565750 },
+ { "81", 571750 },
+ { "82", 577750 },
+ { "83", 583750 },
+ { "84", 589750 },
+ { "85", 595750 },
+ { "86", 601750 },
+ { "87", 607750 },
+ { "88", 613750 },
+ { "89", 619750 },
+ { "90", 625750 },
+ { "91", 631750 },
+ { "92", 637750 },
+ { "93", 643750 },
+ { "94", 649750 },
+ { "95", 97750 },
+ { "96", 103750 },
+ { "97", 109750 },
+ { "98", 115750 },
+ { "99", 121750 },
+ { "100", 655750 },
+ { "101", 661750 },
+ { "102", 667750 },
+ { "103", 673750 },
+ { "104", 679750 },
+ { "105", 685750 },
+ { "106", 691750 },
+ { "107", 697750 },
+ { "108", 703750 },
+ { "109", 709750 },
+ { "110", 715750 },
+ { "111", 721750 },
+ { "112", 727750 },
+ { "113", 733750 },
+ { "114", 739750 },
+ { "115", 745750 },
+ { "116", 751750 },
+ { "117", 757750 },
+ { "118", 763750 },
+ { "119", 769750 },
+ { "120", 775750 },
+ { "121", 781750 },
+ { "122", 787750 },
+ { "123", 793750 },
+ { "124", 799750 },
+ { "125", 805750 }
+};
+
+/* --------------------------------------------------------------------- */
+
+/* JP broadcast */
+static struct CHANLIST ntsc_bcast_jp[] = {
+ { "1", 91250 },
+ { "2", 97250 },
+ { "3", 103250 },
+ { "4", 171250 },
+ { "5", 177250 },
+ { "6", 183250 },
+ { "7", 189250 },
+ { "8", 193250 },
+ { "9", 199250 },
+ { "10", 205250 },
+ { "11", 211250 },
+ { "12", 217250 },
+
+ { "13", 471250 },
+ { "14", 477250 },
+ { "15", 483250 },
+ { "16", 489250 },
+ { "17", 495250 },
+ { "18", 501250 },
+ { "19", 507250 },
+ { "20", 513250 },
+ { "21", 519250 },
+ { "22", 525250 },
+ { "23", 531250 },
+ { "24", 537250 },
+ { "25", 543250 },
+ { "26", 549250 },
+ { "27", 555250 },
+ { "28", 561250 },
+ { "29", 567250 },
+ { "30", 573250 },
+ { "31", 579250 },
+ { "32", 585250 },
+ { "33", 591250 },
+ { "34", 597250 },
+ { "35", 603250 },
+ { "36", 609250 },
+ { "37", 615250 },
+ { "38", 621250 },
+ { "39", 627250 },
+ { "40", 633250 },
+ { "41", 639250 },
+ { "42", 645250 },
+ { "43", 651250 },
+ { "44", 657250 },
+
+ { "45", 663250 },
+ { "46", 669250 },
+ { "47", 675250 },
+ { "48", 681250 },
+ { "49", 687250 },
+ { "50", 693250 },
+ { "51", 699250 },
+ { "52", 705250 },
+ { "53", 711250 },
+ { "54", 717250 },
+ { "55", 723250 },
+ { "56", 729250 },
+ { "57", 735250 },
+ { "58", 741250 },
+ { "59", 747250 },
+ { "60", 753250 },
+ { "61", 759250 },
+ { "62", 765250 },
+};
+
+/* JP cable */
+static struct CHANLIST ntsc_cable_jp[] = {
+ { "13", 109250 },
+ { "14", 115250 },
+ { "15", 121250 },
+ { "16", 127250 },
+ { "17", 133250 },
+ { "18", 139250 },
+ { "19", 145250 },
+ { "20", 151250 },
+
+ { "21", 157250 },
+ { "22", 165250 },
+ { "23", 223250 },
+ { "24", 231250 },
+ { "25", 237250 },
+ { "26", 243250 },
+ { "27", 249250 },
+ { "28", 253250 },
+ { "29", 259250 },
+ { "30", 265250 },
+ { "31", 271250 },
+ { "32", 277250 },
+ { "33", 283250 },
+ { "34", 289250 },
+ { "35", 295250 },
+ { "36", 301250 },
+ { "37", 307250 },
+ { "38", 313250 },
+ { "39", 319250 },
+ { "40", 325250 },
+ { "41", 331250 },
+ { "42", 337250 },
+ { "43", 343250 },
+ { "44", 349250 },
+ { "45", 355250 },
+ { "46", 361250 },
+ { "47", 367250 },
+ { "48", 373250 },
+ { "49", 379250 },
+ { "50", 385250 },
+ { "51", 391250 },
+ { "52", 397250 },
+ { "53", 403250 },
+ { "54", 409250 },
+ { "55", 415250 },
+ { "56", 421250 },
+ { "57", 427250 },
+ { "58", 433250 },
+ { "59", 439250 },
+ { "60", 445250 },
+ { "61", 451250 },
+ { "62", 457250 },
+ { "63", 463250 },
+};
+
+/* --------------------------------------------------------------------- */
+
+/* australia */
+static struct CHANLIST pal_australia[] = {
+ { "0", 46250 },
+ { "1", 57250 },
+ { "2", 64250 },
+ { "3", 86250 },
+ { "4", 95250 },
+ { "5", 102250 },
+ { "5A", 138250 },
+ { "6", 175250 },
+ { "7", 182250 },
+ { "8", 189250 },
+ { "9", 196250 },
+ { "10", 209250 },
+ { "11", 216250 },
+ { "28", 527250 },
+ { "29", 534250 },
+ { "30", 541250 },
+ { "31", 548250 },
+ { "32", 555250 },
+ { "33", 562250 },
+ { "34", 569250 },
+ { "35", 576250 },
+ { "36", 591250 },
+ { "39", 604250 },
+ { "40", 611250 },
+ { "41", 618250 },
+ { "42", 625250 },
+ { "43", 632250 },
+ { "44", 639250 },
+ { "45", 646250 },
+ { "46", 653250 },
+ { "47", 660250 },
+ { "48", 667250 },
+ { "49", 674250 },
+ { "50", 681250 },
+ { "51", 688250 },
+ { "52", 695250 },
+ { "53", 702250 },
+ { "54", 709250 },
+ { "55", 716250 },
+ { "56", 723250 },
+ { "57", 730250 },
+ { "58", 737250 },
+ { "59", 744250 },
+ { "60", 751250 },
+ { "61", 758250 },
+ { "62", 765250 },
+ { "63", 772250 },
+ { "64", 779250 },
+ { "65", 786250 },
+ { "66", 793250 },
+ { "67", 800250 },
+ { "68", 807250 },
+ { "69", 814250 },
+};
+
+/* --------------------------------------------------------------------- */
+/* europe */
+
+/* CCIR frequencies */
+
+#define FREQ_CCIR_I_III \
+ { "E2", 48250 }, \
+ { "E3", 55250 }, \
+ { "E4", 62250 }, \
+ \
+ { "S01", 69250 }, \
+ { "S02", 76250 }, \
+ { "S03", 83250 }, \
+ \
+ { "E5", 175250 }, \
+ { "E6", 182250 }, \
+ { "E7", 189250 }, \
+ { "E8", 196250 }, \
+ { "E9", 203250 }, \
+ { "E10", 210250 }, \
+ { "E11", 217250 }, \
+ { "E12", 224250 }
+
+#define FREQ_CCIR_SL_SH \
+ { "SE1", 105250 }, \
+ { "SE2", 112250 }, \
+ { "SE3", 119250 }, \
+ { "SE4", 126250 }, \
+ { "SE5", 133250 }, \
+ { "SE6", 140250 }, \
+ { "SE7", 147250 }, \
+ { "SE8", 154250 }, \
+ { "SE9", 161250 }, \
+ { "SE10", 168250 }, \
+ \
+ { "SE11", 231250 }, \
+ { "SE12", 238250 }, \
+ { "SE13", 245250 }, \
+ { "SE14", 252250 }, \
+ { "SE15", 259250 }, \
+ { "SE16", 266250 }, \
+ { "SE17", 273250 }, \
+ { "SE18", 280250 }, \
+ { "SE19", 287250 }, \
+ { "SE20", 294250 }
+
+#define FREQ_CCIR_H \
+ { "S21", 303250 }, \
+ { "S22", 311250 }, \
+ { "S23", 319250 }, \
+ { "S24", 327250 }, \
+ { "S25", 335250 }, \
+ { "S26", 343250 }, \
+ { "S27", 351250 }, \
+ { "S28", 359250 }, \
+ { "S29", 367250 }, \
+ { "S30", 375250 }, \
+ { "S31", 383250 }, \
+ { "S32", 391250 }, \
+ { "S33", 399250 }, \
+ { "S34", 407250 }, \
+ { "S35", 415250 }, \
+ { "S36", 423250 }, \
+ { "S37", 431250 }, \
+ { "S38", 439250 }, \
+ { "S39", 447250 }, \
+ { "S40", 455250 }, \
+ { "S41", 463250 }
+
+/* OIRT frequencies */
+
+#define FREQ_OIRT_I_III \
+ { "R1", 49750 }, \
+ { "R2", 59250 }, \
+ \
+ { "R3", 77250 }, \
+ { "R4", 85250 }, \
+ { "R5", 93250 }, \
+ \
+ { "R6", 175250 }, \
+ { "R7", 183250 }, \
+ { "R8", 191250 }, \
+ { "R9", 199250 }, \
+ { "R10", 207250 }, \
+ { "R11", 215250 }, \
+ { "R12", 223250 }
+
+#define FREQ_OIRT_SL_SH \
+ { "SR1", 111250 }, \
+ { "SR2", 119250 }, \
+ { "SR3", 127250 }, \
+ { "SR4", 135250 }, \
+ { "SR5", 143250 }, \
+ { "SR6", 151250 }, \
+ { "SR7", 159250 }, \
+ { "SR8", 167250 }, \
+ \
+ { "SR11", 231250 }, \
+ { "SR12", 239250 }, \
+ { "SR13", 247250 }, \
+ { "SR14", 255250 }, \
+ { "SR15", 263250 }, \
+ { "SR16", 271250 }, \
+ { "SR17", 279250 }, \
+ { "SR18", 287250 }, \
+ { "SR19", 295250 }
+
+#define FREQ_UHF \
+ { "21", 471250 }, \
+ { "22", 479250 }, \
+ { "23", 487250 }, \
+ { "24", 495250 }, \
+ { "25", 503250 }, \
+ { "26", 511250 }, \
+ { "27", 519250 }, \
+ { "28", 527250 }, \
+ { "29", 535250 }, \
+ { "30", 543250 }, \
+ { "31", 551250 }, \
+ { "32", 559250 }, \
+ { "33", 567250 }, \
+ { "34", 575250 }, \
+ { "35", 583250 }, \
+ { "36", 591250 }, \
+ { "37", 599250 }, \
+ { "38", 607250 }, \
+ { "39", 615250 }, \
+ { "40", 623250 }, \
+ { "41", 631250 }, \
+ { "42", 639250 }, \
+ { "43", 647250 }, \
+ { "44", 655250 }, \
+ { "45", 663250 }, \
+ { "46", 671250 }, \
+ { "47", 679250 }, \
+ { "48", 687250 }, \
+ { "49", 695250 }, \
+ { "50", 703250 }, \
+ { "51", 711250 }, \
+ { "52", 719250 }, \
+ { "53", 727250 }, \
+ { "54", 735250 }, \
+ { "55", 743250 }, \
+ { "56", 751250 }, \
+ { "57", 759250 }, \
+ { "58", 767250 }, \
+ { "59", 775250 }, \
+ { "60", 783250 }, \
+ { "61", 791250 }, \
+ { "62", 799250 }, \
+ { "63", 807250 }, \
+ { "64", 815250 }, \
+ { "65", 823250 }, \
+ { "66", 831250 }, \
+ { "67", 839250 }, \
+ { "68", 847250 }, \
+ { "69", 855250 }
+
+static struct CHANLIST europe_west[] = {
+ FREQ_CCIR_I_III,
+ FREQ_CCIR_SL_SH,
+ FREQ_CCIR_H,
+ FREQ_UHF
+};
+
+static struct CHANLIST europe_east[] = {
+ FREQ_OIRT_I_III,
+ FREQ_OIRT_SL_SH,
+ FREQ_CCIR_I_III,
+ FREQ_CCIR_SL_SH,
+ FREQ_CCIR_H,
+ FREQ_UHF
+};
+
+static struct CHANLIST pal_italy[] = {
+ { "A", 53750 },
+ { "B", 62250 },
+ { "C", 82250 },
+ { "D", 175250 },
+ { "E", 183750 },
+ { "F", 192250 },
+ { "G", 201250 },
+ { "H", 210250 },
+ { "H1", 217250 },
+ { "H2", 224250 },
+ FREQ_UHF
+};
+
+static struct CHANLIST pal_ireland[] = {
+ { "A0", 45750 },
+ { "A1", 48000 },
+ { "A2", 53750 },
+ { "A3", 56000 },
+ { "A4", 61750 },
+ { "A5", 64000 },
+ { "A6", 175250 },
+ { "A7", 176000 },
+ { "A8", 183250 },
+ { "A9", 184000 },
+ { "A10", 191250 },
+ { "A11", 192000 },
+ { "A12", 199250 },
+ { "A13", 200000 },
+ { "A14", 207250 },
+ { "A15", 208000 },
+ { "A16", 215250 },
+ { "A17", 216000 },
+ { "A18", 224000 },
+ { "A19", 232000 },
+ { "A20", 248000 },
+ { "A21", 256000 },
+ { "A22", 264000 },
+ { "A23", 272000 },
+ { "A24", 280000 },
+ { "A25", 288000 },
+ { "A26", 296000 },
+ { "A27", 304000 },
+ { "A28", 312000 },
+ { "A29", 320000 },
+ { "A30", 344000 },
+ { "A31", 352000 },
+ { "A32", 408000 },
+ { "A33", 416000 },
+ { "A34", 448000 },
+ { "A35", 480000 },
+ { "A36", 520000 },
+ FREQ_UHF,
+};
+
+static struct CHANLIST secam_france[] = {
+ { "K01", 47750 },
+ { "K02", 55750 },
+ { "K03", 60500 },
+ { "K04", 63750 },
+ { "K05", 176000 },
+ { "K06", 184000 },
+ { "K07", 192000 },
+ { "K08", 200000 },
+ { "K09", 208000 },
+ { "K10", 216000 },
+ { "KB", 116750 },
+ { "KC", 128750 },
+ { "KD", 140750 },
+ { "KE", 159750 },
+ { "KF", 164750 },
+ { "KG", 176750 },
+ { "KH", 188750 },
+ { "KI", 200750 },
+ { "KJ", 212750 },
+ { "KK", 224750 },
+ { "KL", 236750 },
+ { "KM", 248750 },
+ { "KN", 260750 },
+ { "KO", 272750 },
+ { "KP", 284750 },
+ { "KQ", 296750 },
+ { "H01", 303250 },
+ { "H02", 311250 },
+ { "H03", 319250 },
+ { "H04", 327250 },
+ { "H05", 335250 },
+ { "H06", 343250 },
+ { "H07", 351250 },
+ { "H08", 359250 },
+ { "H09", 367250 },
+ { "H10", 375250 },
+ { "H11", 383250 },
+ { "H12", 391250 },
+ { "H13", 399250 },
+ { "H14", 407250 },
+ { "H15", 415250 },
+ { "H16", 423250 },
+ { "H17", 431250 },
+ { "H18", 439250 },
+ { "H19", 447250 },
+ FREQ_UHF,
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct CHANLIST pal_newzealand[] = {
+ { "1", 45250 },
+ { "2", 55250 },
+ { "3", 62250 },
+ { "4", 175250 },
+ { "5", 182250 },
+ { "6", 189250 },
+ { "7", 196250 },
+ { "8", 203250 },
+ { "9", 210250 },
+ { "10", 217250 },
+ { "11", 224250 },
+ FREQ_UHF,
+};
+
+/* --------------------------------------------------------------------- */
+
+/* China broadcast */
+static struct CHANLIST pal_bcast_cn[] = {
+ { "1", 49750 },
+ { "2", 57750 },
+ { "3", 65750 },
+ { "4", 77250 },
+ { "5", 85250 },
+ { "6", 112250 },
+ { "7", 120250 },
+ { "8", 128250 },
+ { "9", 136250 },
+ { "10", 144250 },
+ { "11", 152250 },
+ { "12", 160250 },
+ { "13", 168250 },
+ { "14", 176250 },
+ { "15", 184250 },
+ { "16", 192250 },
+ { "17", 200250 },
+ { "18", 208250 },
+ { "19", 216250 },
+ { "20", 224250 },
+ { "21", 232250 },
+ { "22", 240250 },
+ { "23", 248250 },
+ { "24", 256250 },
+ { "25", 264250 },
+ { "26", 272250 },
+ { "27", 280250 },
+ { "28", 288250 },
+ { "29", 296250 },
+ { "30", 304250 },
+ { "31", 312250 },
+ { "32", 320250 },
+ { "33", 328250 },
+ { "34", 336250 },
+ { "35", 344250 },
+ { "36", 352250 },
+ { "37", 360250 },
+ { "38", 368250 },
+ { "39", 376250 },
+ { "40", 384250 },
+ { "41", 392250 },
+ { "42", 400250 },
+ { "43", 408250 },
+ { "44", 416250 },
+ { "45", 424250 },
+ { "46", 432250 },
+ { "47", 440250 },
+ { "48", 448250 },
+ { "49", 456250 },
+ { "50", 463250 },
+ { "51", 471250 },
+ { "52", 479250 },
+ { "53", 487250 },
+ { "54", 495250 },
+ { "55", 503250 },
+ { "56", 511250 },
+ { "57", 519250 },
+ { "58", 527250 },
+ { "59", 535250 },
+ { "60", 543250 },
+ { "61", 551250 },
+ { "62", 559250 },
+ { "63", 607250 },
+ { "64", 615250 },
+ { "65", 623250 },
+ { "66", 631250 },
+ { "67", 639250 },
+ { "68", 647250 },
+ { "69", 655250 },
+ { "70", 663250 },
+ { "71", 671250 },
+ { "72", 679250 },
+ { "73", 687250 },
+ { "74", 695250 },
+ { "75", 703250 },
+ { "76", 711250 },
+ { "77", 719250 },
+ { "78", 727250 },
+ { "79", 735250 },
+ { "80", 743250 },
+ { "81", 751250 },
+ { "82", 759250 },
+ { "83", 767250 },
+ { "84", 775250 },
+ { "85", 783250 },
+ { "86", 791250 },
+ { "87", 799250 },
+ { "88", 807250 },
+ { "89", 815250 },
+ { "90", 823250 },
+ { "91", 831250 },
+ { "92", 839250 },
+ { "93", 847250 },
+ { "94", 855250 },
+};
+
+/* --------------------------------------------------------------------- */
+/* South Africa Broadcast */
+
+static struct CHANLIST pal_bcast_za[] ={
+ { "1", 175250 },
+ { "2", 183250 },
+ { "3", 191250 },
+ { "4", 199250 },
+ { "5", 207250 },
+ { "6", 215250 },
+ { "7", 223250 },
+ { "8", 231250 },
+ FREQ_UHF
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct CHANLIST argentina[] = {
+ { "001", 56250 },
+ { "002", 62250 },
+ { "003", 68250 },
+ { "004", 78250 },
+ { "005", 84250 },
+ { "006", 176250 },
+ { "007", 182250 },
+ { "008", 188250 },
+ { "009", 194250 },
+ { "010", 200250 },
+ { "011", 206250 },
+ { "012", 212250 },
+ { "013", 122250 },
+ { "014", 128250 },
+ { "015", 134250 },
+ { "016", 140250 },
+ { "017", 146250 },
+ { "018", 152250 },
+ { "019", 158250 },
+ { "020", 164250 },
+ { "021", 170250 },
+ { "022", 218250 },
+ { "023", 224250 },
+ { "024", 230250 },
+ { "025", 236250 },
+ { "026", 242250 },
+ { "027", 248250 },
+ { "028", 254250 },
+ { "029", 260250 },
+ { "030", 266250 },
+ { "031", 272250 },
+ { "032", 278250 },
+ { "033", 284250 },
+ { "034", 290250 },
+ { "035", 296250 },
+ { "036", 302250 },
+ { "037", 308250 },
+ { "038", 314250 },
+ { "039", 320250 },
+ { "040", 326250 },
+ { "041", 332250 },
+ { "042", 338250 },
+ { "043", 344250 },
+ { "044", 350250 },
+ { "045", 356250 },
+ { "046", 362250 },
+ { "047", 368250 },
+ { "048", 374250 },
+ { "049", 380250 },
+ { "050", 386250 },
+ { "051", 392250 },
+ { "052", 398250 },
+ { "053", 404250 },
+ { "054", 410250 },
+ { "055", 416250 },
+ { "056", 422250 },
+ { "057", 428250 },
+ { "058", 434250 },
+ { "059", 440250 },
+ { "060", 446250 },
+ { "061", 452250 },
+ { "062", 458250 },
+ { "063", 464250 },
+ { "064", 470250 },
+ { "065", 476250 },
+ { "066", 482250 },
+ { "067", 488250 },
+ { "068", 494250 },
+ { "069", 500250 },
+ { "070", 506250 },
+ { "071", 512250 },
+ { "072", 518250 },
+ { "073", 524250 },
+ { "074", 530250 },
+ { "075", 536250 },
+ { "076", 542250 },
+ { "077", 548250 },
+ { "078", 554250 },
+ { "079", 560250 },
+ { "080", 566250 },
+ { "081", 572250 },
+ { "082", 578250 },
+ { "083", 584250 },
+ { "084", 590250 },
+ { "085", 596250 },
+ { "086", 602250 },
+ { "087", 608250 },
+ { "088", 614250 },
+ { "089", 620250 },
+ { "090", 626250 },
+ { "091", 632250 },
+ { "092", 638250 },
+ { "093", 644250 },
+};
+
+/* --------------------------------------------------------------------- */
+
+struct CHANLISTS chanlists[] = {
+ { "us-bcast", ntsc_bcast, CHAN_COUNT(ntsc_bcast) },
+ { "us-cable", ntsc_cable, CHAN_COUNT(ntsc_cable) },
+ { "us-cable-hrc", ntsc_hrc, CHAN_COUNT(ntsc_hrc) },
+ { "japan-bcast", ntsc_bcast_jp, CHAN_COUNT(ntsc_bcast_jp) },
+ { "japan-cable", ntsc_cable_jp, CHAN_COUNT(ntsc_cable_jp) },
+ { "europe-west", europe_west, CHAN_COUNT(europe_west) },
+ { "europe-east", europe_east, CHAN_COUNT(europe_east) },
+ { "italy", pal_italy, CHAN_COUNT(pal_italy) },
+ { "newzealand", pal_newzealand, CHAN_COUNT(pal_newzealand) },
+ { "australia", pal_australia, CHAN_COUNT(pal_australia) },
+ { "ireland", pal_ireland, CHAN_COUNT(pal_ireland) },
+ { "france", secam_france, CHAN_COUNT(secam_france) },
+ { "china-bcast", pal_bcast_cn, CHAN_COUNT(pal_bcast_cn) },
+ { "southafrica", pal_bcast_za, CHAN_COUNT(pal_bcast_za) },
+ { "argentina", argentina, CHAN_COUNT(argentina) },
+ { "canada-cable", ntsc_cable_ca, CHAN_COUNT(ntsc_cable_ca) },
+ { NULL, NULL, 0 } /* EOF */
+};
+
+struct STRTAB chanlist_names[] = {
+ { 0, "us-bcast" },
+ { 1, "us-cable" },
+ { 2, "us-cable-hrc" },
+ { 3, "japan-bcast" },
+ { 4, "japan-cable" },
+ { 5, "europe-west" },
+ { 6, "europe-east" },
+ { 7, "italy" },
+ { 8, "newzealand" },
+ { 9, "australia" },
+ { 10, "ireland" },
+ { 11, "france" },
+ { 12, "china-bcast" },
+ { 13, "southafrica" },
+ { 14, "argentina" },
+ { 15, "canada-cable" },
+ { -1, NULL }
+};
+
+int chantab = 5;
+struct CHANLIST *chanlist = europe_west;
+int chancount = CHAN_COUNT(europe_west);
+
diff --git a/common/frequencies.h b/common/frequencies.h
new file mode 100644
index 0000000..8f62e75
--- /dev/null
+++ b/common/frequencies.h
@@ -0,0 +1,111 @@
+/*
+ * Worldwide channel/frequency list
+ *
+ * Nathan Laredo (laredo@broked.net)
+ *
+ * Frequencies are given in kHz
+ */
+#define NTSC_AUDIO_CARRIER 4500
+#define PAL_AUDIO_CARRIER_I 6000
+#define PAL_AUDIO_CARRIER_BGHN 5500
+#define PAL_AUDIO_CARRIER_MN 4500
+#define PAL_AUDIO_CARRIER_D 6500
+#define SEACAM_AUDIO_DKK1L 6500
+#define SEACAM_AUDIO_BG 5500
+/* NICAM 728 32-kHz, 14-bit digital stereo audio is transmitted in 1ms frames
+ containing 8 bits frame sync, 5 bits control, 11 bits additional data, and
+ 704 bits audio data. The bit rate is reduced by transmitting only 10 bits
+ plus parity of each 14 bit sample, the largest sample in a frame determines
+ which 10 bits are transmitted. The parity bits for audio samples also
+ specify the scaling factor used for that channel during that frame. The
+ companeded audio data is interleaved to reduce the influence of dropouts
+ and the whole frame except for sync bits is scrambled for spectrum shaping.
+ Data is modulated using QPSK, at below following subcarrier freqs */
+#define NICAM728_PAL_BGH 5850
+#define NICAM728_PAL_I 6552
+
+/* COMPREHENSIVE LIST OF FORMAT BY COUNTRY
+ (M) NTSC used in:
+ Antigua, Aruba, Bahamas, Barbados, Belize, Bermuda, Bolivia, Burma,
+ Canada, Chile, Colombia, Costa Rica, Cuba, Curacao, Dominican Republic,
+ Ecuador, El Salvador, Guam Guatemala, Honduras, Jamaica, Japan,
+ South Korea, Mexico, Montserrat, Myanmar, Nicaragua, Panama, Peru,
+ Philippines, Puerto Rico, St Christopher and Nevis, Samoa, Suriname,
+ Taiwan, Trinidad/Tobago, United States, Venezuela, Virgin Islands
+ (B) PAL used in:
+ Albania, Algeria, Australia, Austria, Bahrain, Bangladesh, Belgium,
+ Bosnia-Herzegovinia, Brunei Darussalam, Cambodia, Cameroon, Croatia,
+ Cyprus, Denmark, Egypt, Ethiopia, Equatorial Guinea, Finland, Germany,
+ Ghana, Gibraltar, Greenland, Iceland, India, Indonesia, Israel, Italy,
+ Jordan, Kenya, Kuwait, Liberia, Libya, Luxembourg, Malaysa, Maldives,
+ Malta, Nepal, Netherlands, New Zeland, Nigeria, Norway, Oman, Pakistan,
+ Papua New Guinea, Portugal, Qatar, Sao Tome and Principe, Saudi Arabia,
+ Seychelles, Sierra Leone, Singapore, Slovenia, Somali, Spain,
+ Sri Lanka, Sudan, Swaziland, Sweden, Switzeland, Syria, Thailand,
+ Tunisia, Turkey, Uganda, United Arab Emirates, Yemen
+ (N) PAL used in: (Combination N = 4.5MHz audio carrier, 3.58MHz burst)
+ Argentina (Combination N), Paraguay, Uruguay
+ (M) PAL (525/60, 3.57MHz burst) used in:
+ Brazil
+ (G) PAL used in:
+ Albania, Algeria, Austria, Bahrain, Bosnia/Herzegovinia, Cambodia,
+ Cameroon, Croatia, Cyprus, Denmark, Egypt, Ethiopia, Equatorial Guinea,
+ Finland, Germany, Gibraltar, Greenland, Iceland, Israel, Italy, Jordan,
+ Kenya, Kuwait, Liberia, Libya, Luxembourg, Malaysia, Monaco,
+ Mozambique, Netherlands, New Zealand, Norway, Oman, Pakistan,
+ Papa New Guinea, Portugal, Qatar, Romania, Sierra Leone, Singapore,
+ Slovenia, Somalia, Spain, Sri Lanka, Sudan, Swaziland, Sweeden,
+ Switzerland, Syria, Thailand, Tunisia, Turkey, United Arab Emirates,
+ Yemen, Zambia, Zimbabwe
+ (D) PAL used in:
+ China, North Korea, Romania, Czech Republic
+ (H) PAL used in:
+ Belgium
+ (I) PAL used in:
+ Angola, Botswana, Gambia, Guinea-Bissau, Hong Kong, Ireland, Lesotho,
+ Malawi, Nambia, Nigeria, South Africa, Tanzania, United Kingdom,
+ Zanzibar
+ (B) SECAM used in:
+ Djibouti, Greece, Iran, Iraq, Lebanon, Mali, Mauritania, Mauritus,
+ Morocco
+ (D) SECAM used in:
+ Afghanistan, Armenia, Azerbaijan, Belarus, Bulgaria,
+ Estonia, Georgia, Hungary, Zazakhstan, Lithuania, Mongolia, Moldova,
+ Russia, Slovak Republic, Ukraine, Vietnam
+ (G) SECAM used in:
+ Greecem Iran, Iraq, Mali, Mauritus, Morocco, Saudi Arabia
+ (K) SECAM used in:
+ Armenia, Azerbaijan, Bulgaria, Estonia, Georgia,
+ Hungary, Kazakhstan, Lithuania, Madagascar, Moldova, Poland, Russia,
+ Slovak Republic, Ukraine, Vietnam
+ (K1) SECAM used in:
+ Benin, Burkina Faso, Burundi, Chad, Cape Verde, Central African
+ Republic, Comoros, Congo, Gabon, Madagascar, Niger, Rwanda, Senegal,
+ Togo, Zaire
+ (L) SECAM used in:
+ France
+*/
+
+/* --------------------------------------------------------------------- */
+
+struct CHANLIST {
+ char *name;
+ int freq;
+};
+
+struct CHANLISTS {
+ char *name;
+ struct CHANLIST *list;
+ int count;
+};
+
+#define CHAN_COUNT(x) (sizeof(x)/sizeof(struct CHANLIST))
+
+/* --------------------------------------------------------------------- */
+
+extern struct CHANLISTS chanlists[];
+extern struct STRTAB chanlist_names[];
+
+extern int chantab;
+extern struct CHANLIST *chanlist;
+extern int chancount;
diff --git a/common/joystick.c b/common/joystick.c
new file mode 100644
index 0000000..69ed3fe
--- /dev/null
+++ b/common/joystick.c
@@ -0,0 +1,105 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_LINUX_JOYSTICK_H
+# include <linux/joystick.h>
+#endif
+
+#include "grab-ng.h"
+#include "commands.h"
+#include "joystick.h"
+#include "event.h"
+
+/*-----------------------------------------------------------------------*/
+
+extern int debug;
+
+#ifdef HAVE_LINUX_JOYSTICK_H
+struct JOYTAB {
+ int class;
+ int number;
+ int value;
+ char *event;
+};
+
+static struct JOYTAB joytab[] = {
+ { JS_EVENT_BUTTON, 0, 1, "joy-button-0" },
+ { JS_EVENT_BUTTON, 1, 1, "joy-button-1" },
+ { JS_EVENT_AXIS, 1, -32767, "joy-axis-up" },
+ { JS_EVENT_AXIS, 1, 32767, "joy-axis-down" },
+ { JS_EVENT_AXIS, 0, 32767, "joy-axis-left" },
+ { JS_EVENT_AXIS, 0, -32767, "joy-axis-right" },
+};
+#define NJOYTAB (sizeof(joytab)/sizeof(struct JOYTAB))
+
+static struct event_entry joy_events[] = {
+ {
+ event: "joy-button-0",
+ action: "quit",
+ },{
+ event: "joy-button-1",
+ action: "fullscreen",
+ },{
+ event: "joy-axis-up",
+ action: "volume inc",
+ },{
+ event: "joy-axis-down",
+ action: "volume dec",
+ },{
+ event: "joy-axis-left",
+ action: "setchannel prev",
+ },{
+ event: "joy-axis-right",
+ action: "setchannel next",
+ },{
+ /* end of list */
+ }
+};
+
+#endif
+
+int joystick_tv_init(char *dev)
+{
+#ifdef HAVE_LINUX_JOYSTICK_H
+ int fd;
+
+ if (NULL == dev)
+ return -1;
+ if (-1 == (fd = open(dev, O_NONBLOCK))) {
+ fprintf(stderr, "joystick: open %s: %s\n",dev,strerror(errno));
+ return -1;
+ }
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ event_register_list(joy_events);
+ return fd;
+#else
+ return -1;
+#endif
+}
+
+void joystick_tv_havedata(int js)
+{
+#ifdef HAVE_LINUX_JOYSTICK_H
+ int i;
+ struct js_event event;
+ if (debug)
+ fprintf(stderr, "joystick: received input\n");
+ if (read(js, &event, sizeof(struct js_event))) {
+ for (i = 0; i < NJOYTAB; i++)
+ if (joytab[i].class == (event.type)
+ && joytab[i].number == event.number
+ && joytab[i].value == event.value)
+ break;
+ if (i != NJOYTAB)
+ event_dispatch(joytab[i].event);
+ }
+#endif
+}
diff --git a/common/joystick.h b/common/joystick.h
new file mode 100644
index 0000000..ccf627e
--- /dev/null
+++ b/common/joystick.h
@@ -0,0 +1,2 @@
+int joystick_tv_init(char *dev);
+void joystick_tv_havedata(int js);
diff --git a/common/lirc.c b/common/lirc.c
new file mode 100644
index 0000000..a39f5ab
--- /dev/null
+++ b/common/lirc.c
@@ -0,0 +1,154 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include "config.h"
+
+#ifdef HAVE_LIBLIRC_CLIENT
+# include <lirc/lirc_client.h>
+#endif
+#include "grab-ng.h"
+#include "commands.h"
+#include "lirc.h"
+#include "event.h"
+
+/*-----------------------------------------------------------------------*/
+
+extern int debug;
+
+#ifdef HAVE_LIBLIRC_CLIENT
+static struct lirc_config *config = NULL;
+
+static struct event_entry lirc_events[] = {
+ {
+ event: "lirc-key-ch+",
+ action: "setstation next",
+ },{
+ event: "lirc-key-ch-",
+ action: "setstation prev",
+ },{
+ event: "lirc-key-vol+",
+ action: "volume inc",
+ },{
+ event: "lirc-key-vol-",
+ action: "volume dec",
+ },{
+ event: "lirc-key-mute",
+ action: "volume mute",
+ },{
+ event: "lirc-key-full_screen",
+ action: "fullscreen toggle",
+ },{
+ event: "lirc-key-source",
+ action: "setinput next",
+ },{
+ event: "lirc-key-reserved",
+ action: "quit",
+ },{
+ event: "lirc-key-0",
+ action: "keypad 0",
+ },{
+ event: "lirc-key-1",
+ action: "keypad 1",
+ },{
+ event: "lirc-key-2",
+ action: "keypad 2",
+ },{
+ event: "lirc-key-3",
+ action: "keypad 3",
+ },{
+ event: "lirc-key-4",
+ action: "keypad 4",
+ },{
+ event: "lirc-key-5",
+ action: "keypad 5",
+ },{
+ event: "lirc-key-6",
+ action: "keypad 6",
+ },{
+ event: "lirc-key-7",
+ action: "keypad 7",
+ },{
+ event: "lirc-key-8",
+ action: "keypad 8",
+ },{
+ event: "lirc-key-9",
+ action: "keypad 9",
+ },{
+ /* end of list */
+ }
+};
+#endif
+
+int lirc_tv_init()
+{
+#ifdef HAVE_LIBLIRC_CLIENT
+ int fd;
+
+ if (-1 == (fd = lirc_init("xawtv",debug))) {
+ if (debug)
+ fprintf(stderr,"lirc: no infrared remote support available\n");
+ return -1;
+ }
+ if (0 != lirc_readconfig(NULL,&config,NULL)) {
+ config = NULL;
+ }
+ if (debug)
+ fprintf(stderr, "lirc: ~/.lircrc file %sfound\n", config?"":"not ");
+
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ event_register_list(lirc_events);
+ if (debug)
+ fprintf(stderr,"lirc: init ok\n");
+
+ return fd;
+#else
+ return -1;
+#endif
+}
+
+int lirc_tv_havedata()
+{
+#ifdef HAVE_LIBLIRC_CLIENT
+ char *code,event[32],*cmd,**argv;
+ int dummy,repeat,argc;
+ int ret=-1;
+
+ strcpy(event,"lirc-key-");
+ while (lirc_nextcode(&code)==0 && code!=NULL) {
+ ret = 0;
+ if (3 != sscanf(code,"%x %x %20s",&dummy,&repeat,event+9)) {
+ fprintf(stderr,"lirc: oops, parse error: %s",code);
+ continue;
+ }
+ if (debug)
+ fprintf(stderr,"lirc: key=%s repeat=%d\n", event+9, repeat);
+ if (config) {
+ /* use ~/.lircrc */
+ while (lirc_code2char(config,code,&cmd)==0 && cmd != NULL) {
+ if (debug)
+ fprintf(stderr,"lirc: cmd \"%s\"\n", cmd);
+ if (0 == strcasecmp(cmd,"eventmap")) {
+ event_dispatch(event);
+ } else {
+ argv = split_cmdline(cmd,&argc);
+ do_command(argc,argv);
+ }
+ }
+ } else {
+ /* standalone mode */
+ if (!repeat)
+ event_dispatch(event);
+ }
+ free(code);
+ }
+ return ret;
+#else
+ return 0;
+#endif
+}
diff --git a/common/lirc.h b/common/lirc.h
new file mode 100644
index 0000000..9906c81
--- /dev/null
+++ b/common/lirc.h
@@ -0,0 +1,2 @@
+int lirc_tv_init(void);
+int lirc_tv_havedata(void);
diff --git a/common/midictrl.c b/common/midictrl.c
new file mode 100644
index 0000000..42f33c0
--- /dev/null
+++ b/common/midictrl.c
@@ -0,0 +1,311 @@
+#include "config.h"
+
+#ifdef HAVE_ALSA
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <X11/Intrinsic.h>
+
+#include "grab-ng.h"
+#include "commands.h"
+#include "channel.h"
+#include "midictrl.h"
+#include "event.h"
+
+extern int debug;
+
+/* ------------------------------------------------------------------------ */
+
+static char *midi_events[] = {
+ "system", "result",
+ "r2", "r3", "r4",
+ "note", "noteon", "noteoff", "keypress",
+ "r9",
+ "controller", "pgmchange", "chanpress", "pitchbend",
+ "control14", "nonregparam", "regparam",
+ "r17", "r18", "r19",
+ "songpos", "songsel", "qframe", "timesign", "keysign",
+ "r25", "r26", "r27", "r28", "r29",
+ "start", "continue", "stop", "setpos_tick", "setpos_time",
+ "tempo", "clock", "tick",
+ "r38", "r39",
+ "tune_request", "reset", "sensing",
+ "r43", "r44", "r45", "r46", "r47", "r48", "r49",
+ "echo", "oss",
+ "r52", "r53", "r54", "r55", "r56", "r57", "r58", "r59",
+ "client_start", "client_exit", "client_change",
+ "port_start", "port_exit", "port_change",
+ "subscribed", "used", "unsubscribed", "unused",
+ "sample", "sample_cluster", "sample_start", "sample_stop",
+ "sample_freq", "sample_volume", "sample_loop",
+ "sample_position", "sample_private1",
+ "r79",
+ "r80", "r81", "r82", "r83", "r84", "r85", "r86", "r87", "r88", "r89",
+ "usr0", "usr1", "usr2", "usr3", "usr4",
+ "usr5", "usr6", "usr7", "usr8", "usr9",
+ "instr_begin", "instr_end", "instr_info", "instr_info_result",
+ "instr_finfo", "instr_finfo_result", "instr_reset", "instr_status",
+ "instr_status_result", "instr_put", "instr_get", "instr_get_result",
+ "instr_free", "instr_list", "instr_list_result", "instr_cluster",
+ "instr_cluster_get", "instr_cluster_result", "instr_change",
+ "r119",
+ "r120", "r121", "r122", "r123", "r124",
+ "r125", "r126", "r127", "r128", "r129",
+ "sysext", "bounce",
+ "r132", "r133", "r134",
+ "usr_var0", "usr_var1", "usr_var2", "usr_var3", "usr_var4",
+ "ipcshm",
+ "r141", "r142", "r143", "r144",
+ "usr_varipc0", "usr_varipc1", "usr_varipc2", "usr_varipc3", "usr_varipc4",
+ "kernel_error", "kernel_quote"
+};
+#define EV_NAME(x) (x < sizeof(midi_events)/sizeof(char*) ? \
+ midi_events[x] : "UNKNOWN")
+
+static void
+midi_dump_ev(FILE *out, snd_seq_event_t *ev)
+{
+ fprintf(out,"midi ev:");
+ if (ev->flags & SND_SEQ_TIME_STAMP_TICK)
+ fprintf(out," tick %d",ev->time.tick);
+ if (ev->flags & SND_SEQ_TIME_STAMP_REAL) {
+ fprintf(out," real %d:%06d",
+ ev->time.time.tv_sec,ev->time.time.tv_nsec);
+ }
+ if (ev->flags & SND_SEQ_TIME_MODE_ABS)
+ fprintf(out," abs");
+ if (ev->flags & SND_SEQ_TIME_MODE_REL)
+ fprintf(out," rel");
+
+ fprintf(out," [%d:%d] %s",
+ ev->source.client,ev->source.port,EV_NAME(ev->type));
+
+ switch (ev->type) {
+ case SND_SEQ_EVENT_NOTE:
+ fprintf(out," ch=%d note=%d vel=%d off_vel=%d dur=%d",
+ ev->data.note.channel,
+ ev->data.note.note,
+ ev->data.note.velocity,
+ ev->data.note.off_velocity,
+ ev->data.note.duration);
+ break;
+ case SND_SEQ_EVENT_NOTEON:
+ case SND_SEQ_EVENT_NOTEOFF:
+ case SND_SEQ_EVENT_KEYPRESS:
+ fprintf(out," ch=%d note=%d vel=%d",
+ ev->data.note.channel,
+ ev->data.note.note,
+ ev->data.note.velocity);
+ break;
+ case SND_SEQ_EVENT_CONTROLLER:
+ case SND_SEQ_EVENT_PGMCHANGE:
+ case SND_SEQ_EVENT_CONTROL14:
+ case SND_SEQ_EVENT_NONREGPARAM:
+ case SND_SEQ_EVENT_REGPARAM:
+ fprintf(out," ch=%d par=%d val=%d",
+ ev->data.control.channel,
+ ev->data.control.param,
+ ev->data.control.value);
+ break;
+ case SND_SEQ_EVENT_CHANPRESS:
+ case SND_SEQ_EVENT_PITCHBEND:
+ fprintf(out," ch=%d val=%d",
+ ev->data.control.channel,
+ ev->data.control.value);
+ break;
+ case SND_SEQ_EVENT_SONGPOS:
+ case SND_SEQ_EVENT_SONGSEL:
+ case SND_SEQ_EVENT_QFRAME:
+ case SND_SEQ_EVENT_TIMESIGN:
+ case SND_SEQ_EVENT_KEYSIGN:
+ fprintf(out," val=%d",
+ ev->data.control.value);
+ break;
+ }
+ fprintf(out,"\n");
+}
+
+/* ------------------------------------------------------------------------ */
+
+int midi_open(struct midi_handle *h, char *name)
+{
+ char *func;
+ int rc;
+
+ func = "snd_seq_open";
+#if SND_LIB_VERSION >= 0x000900
+ if ((rc = snd_seq_open(&h->seq, "default", SND_SEQ_OPEN_INPUT,
+ SND_SEQ_NONBLOCK)) < 0)
+ goto err;
+#else
+ if ((rc = snd_seq_open(&h->seq, SND_SEQ_OPEN_IN)) < 0)
+ goto err;
+#endif
+
+ func = "snd_seq_set_client_name";
+ if ((rc = snd_seq_set_client_name(h->seq, name)) < 0)
+ goto err;
+
+ func = "snd_seq_create_simple_port";
+ if ((rc = snd_seq_create_simple_port
+ (h->seq,"name",
+ SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
+ SND_SEQ_PORT_TYPE_APPLICATION)) < 0)
+ goto err;
+ h->port = rc;
+
+ if (debug)
+ fprintf(stderr,"midi: open ok [%d:%d]\n",
+ snd_seq_client_id(h->seq),h->port);
+#if SND_LIB_VERSION >= 0x000900
+ {
+ struct pollfd p;
+ snd_seq_poll_descriptors(h->seq, &p, 1, POLLIN);
+ h->fd = p.fd;
+ }
+#else
+ h->fd = snd_seq_file_descriptor(h->seq);
+#endif
+ return h->fd;
+
+ err:
+ fprintf(stderr, "midi: %s: %s\n",func,snd_strerror(rc));
+ if (h->seq) {
+ snd_seq_close(h->seq);
+ h->seq = NULL;
+ }
+ return -1;
+}
+
+int midi_close(struct midi_handle *h)
+{
+ if (debug)
+ fprintf(stderr,"midi: close\n");
+ if (h->ev) {
+ snd_seq_free_event(h->ev);
+ h->ev = NULL;
+ }
+ if (h->seq) {
+ snd_seq_close(h->seq);
+ h->seq = NULL;
+ }
+ return 0;
+}
+
+int midi_connect(struct midi_handle *h, char *arg)
+{
+ int client, port, rc;
+ snd_seq_port_subscribe_t *psubs;
+#if SND_LIB_VERSION >= 0x000900
+ snd_seq_addr_t addr;
+#else
+ snd_seq_port_subscribe_t subs;
+#endif
+
+ if (2 == sscanf(arg,"%d:%d",&client,&port)) {
+ /* nothing */
+ } else {
+ return -1;
+ }
+#if SND_LIB_VERSION >= 0x000900
+ snd_seq_port_subscribe_malloc(&psubs);
+ addr.client = client;
+ addr.port = port;
+ snd_seq_port_subscribe_set_sender(psubs,&addr);
+ addr.client = snd_seq_client_id(h->seq);
+ addr.port = h->port;
+ snd_seq_port_subscribe_set_dest(psubs,&addr);
+#else
+ psubs = &subs;
+ memset(&subs,0,sizeof(subs));
+ subs.sender.client = client;
+ subs.sender.port = port;
+ subs.dest.client = snd_seq_client_id(h->seq);
+ subs.dest.port = h->port;
+#endif
+ if ((rc = snd_seq_subscribe_port(h->seq, psubs)) < 0) {
+ fprintf(stderr, "midi: snd_seq_subscribe_port: %s\n",snd_strerror(rc));
+ } else
+ if (debug)
+ fprintf(stderr,"midi: subscribe ok [%d:%d]\n",client,port);
+
+#if SND_LIB_VERSION >= 0x000900
+ snd_seq_port_subscribe_free(psubs);
+#endif
+ return rc;
+}
+
+int midi_read(struct midi_handle *h)
+{
+ int rc;
+
+ if (h->ev) {
+ snd_seq_free_event(h->ev);
+ h->ev = NULL;
+ }
+ if ((rc = snd_seq_event_input(h->seq,&h->ev)) < 0) {
+ fprintf(stderr, "midi: snd_seq_event_input: %s\n",snd_strerror(rc));
+ return -1;
+ }
+ if (debug > 1)
+ midi_dump_ev(stderr,h->ev);
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#ifdef STANDALONE
+
+int debug = 2;
+
+int main(int argc, char *argv[])
+{
+ struct midi_handle h;
+
+ memset(&h,0,sizeof(h));
+ if (-1 == midi_open(&h, "midi dump"))
+ exit(1);
+
+ if (argc > 1)
+ midi_connect(&h,argv[1]);
+
+ for (;;)
+ midi_read(&h);
+
+ midi_close(&h);
+ exit(0);
+}
+
+#else /* STANDALONE */
+
+void midi_translate(struct midi_handle *h)
+{
+ char event[64];
+ int i;
+
+ switch (h->ev->type) {
+ case SND_SEQ_EVENT_NOTEON:
+ if (0 == h->ev->data.note.velocity)
+ return;
+ for (i = 0; i < count; i++) {
+ if (channels[i]->midi != 0 &&
+ channels[i]->midi == h->ev->data.note.note) {
+ do_va_cmd(2,"setstation",channels[i]->name);
+ return;
+ }
+ }
+ sprintf(event,"midi-note-%d",h->ev->data.note.note);
+ event_dispatch(event);
+ break;
+ case SND_SEQ_EVENT_CONTROLLER:
+ sprintf(event,"midi-ctrl-%d(%d%%)",
+ h->ev->data.control.param,
+ h->ev->data.control.value*100/128);
+ event_dispatch(event);
+ }
+}
+
+#endif /* STANDALONE */
+#endif /* HAVE_ALSA */
diff --git a/common/midictrl.h b/common/midictrl.h
new file mode 100644
index 0000000..ecd29e2
--- /dev/null
+++ b/common/midictrl.h
@@ -0,0 +1,23 @@
+#ifdef HAVE_ALSA
+
+#ifdef HAVE_ALSA_ASOUNDLIB_H
+# include <alsa/asoundlib.h>
+#else
+# include <sys/asoundlib.h>
+#endif
+
+struct midi_handle {
+ snd_seq_t *seq;
+ int fd;
+ int port;
+ snd_seq_event_t *ev;
+};
+
+int midi_open(struct midi_handle *h, char *name);
+int midi_close(struct midi_handle *h);
+int midi_connect(struct midi_handle *h, char *arg);
+int midi_read(struct midi_handle *h);
+
+void midi_translate(struct midi_handle *h);
+
+#endif
diff --git a/common/parseconfig.c b/common/parseconfig.c
new file mode 100644
index 0000000..7a0dd66
--- /dev/null
+++ b/common/parseconfig.c
@@ -0,0 +1,216 @@
+/*
+ * config file parser
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "parseconfig.h"
+
+struct CFG_ENTRIES {
+ int ent_count;
+ char **ent_names;
+ char **ent_values;
+ int **ent_seen;
+};
+
+struct CFG_SECTIONS {
+ int sec_count;
+ char **sec_names;
+ struct CFG_ENTRIES **sec_entries;
+};
+
+/* ------------------------------------------------------------------------ */
+
+static struct CFG_SECTIONS *c;
+
+/* ------------------------------------------------------------------------ */
+
+#define ALLOC_SIZE 16
+
+static struct CFG_SECTIONS*
+cfg_init_sections(void)
+{
+ struct CFG_SECTIONS *c;
+ c = malloc(sizeof(struct CFG_SECTIONS));
+ memset(c,0,sizeof(struct CFG_SECTIONS));
+ c->sec_names = malloc(ALLOC_SIZE*sizeof(char*));
+ c->sec_names[0] = NULL;
+ c->sec_entries = malloc(ALLOC_SIZE*sizeof(struct CFG_ENTRIES*));
+ c->sec_entries[0] = NULL;
+ return c;
+}
+
+static struct CFG_ENTRIES*
+cfg_init_entries(void)
+{
+ struct CFG_ENTRIES *e;
+ e = malloc(sizeof(struct CFG_ENTRIES));
+ memset(e,0,sizeof(struct CFG_ENTRIES));
+ e->ent_names = malloc(ALLOC_SIZE*sizeof(char*));
+ e->ent_names[0] = NULL;
+ e->ent_values = malloc(ALLOC_SIZE*sizeof(char*));
+ e->ent_values[0] = NULL;
+ e->ent_seen = malloc(ALLOC_SIZE*sizeof(int));
+ e->ent_seen[0] = 0;
+ return e;
+}
+
+static struct CFG_ENTRIES*
+cfg_find_section(struct CFG_SECTIONS *c, char *name)
+{
+ struct CFG_ENTRIES* e;
+ int i;
+
+ for (i = 0; i < c->sec_count; i++)
+ if (0 == strcasecmp(c->sec_names[i],name))
+ return c->sec_entries[i];
+
+ /* 404 not found => create a new one */
+ if ((c->sec_count % ALLOC_SIZE) == (ALLOC_SIZE-2)) {
+ c->sec_names = realloc(c->sec_names,(c->sec_count+2+ALLOC_SIZE)*sizeof(char*));
+ c->sec_entries = realloc(c->sec_entries,(c->sec_count+2+ALLOC_SIZE)*sizeof(struct CFG_ENTRIES*));
+ }
+ e = cfg_init_entries();
+ c->sec_names[c->sec_count] = strdup(name);
+ c->sec_entries[c->sec_count] = e;
+ c->sec_count++;
+ c->sec_names[c->sec_count] = NULL;
+ c->sec_entries[c->sec_count] = NULL;
+ return e;
+}
+
+static void
+cfg_set_entry(struct CFG_ENTRIES *e, char *name, char *value)
+{
+ int i;
+
+ for (i = 0; i < e->ent_count; i++)
+ if (0 == strcasecmp(e->ent_names[i],name))
+ break;
+ if (i == e->ent_count) {
+ /* 404 not found => create a new one */
+ if ((e->ent_count % ALLOC_SIZE) == (ALLOC_SIZE-2)) {
+ e->ent_names = realloc(e->ent_names,(e->ent_count+2+ALLOC_SIZE)*sizeof(char*));
+ e->ent_values = realloc(e->ent_values,(e->ent_count+2+ALLOC_SIZE)*sizeof(char*));
+ e->ent_seen = realloc(e->ent_seen,(e->ent_count+2+ALLOC_SIZE)*sizeof(int));
+ }
+ e->ent_count++;
+ e->ent_names[e->ent_count] = NULL;
+ e->ent_values[e->ent_count] = NULL;
+ e->ent_seen[e->ent_count] = 0;
+ }
+ e->ent_names[i] = strdup(name);
+ e->ent_values[i] = strdup(value);
+}
+
+/* ------------------------------------------------------------------------ */
+
+int
+cfg_parse_file(char *filename)
+{
+ struct CFG_ENTRIES *e = NULL;
+ char line[256],tag[64],value[192];
+ FILE *fp;
+ int nr;
+
+ if (NULL == c)
+ c = cfg_init_sections();
+ if (NULL == (fp = fopen(filename,"r")))
+ return -1;
+
+ nr = 0;
+ while (NULL != fgets(line,255,fp)) {
+ nr++;
+ if (line[0] == '\n' || line[0] == '#' || line[0] == '%')
+ continue;
+ if (1 == sscanf(line,"[%99[^]]]",value)) {
+ /* [section] */
+ e = cfg_find_section(c,value);
+ } else if (2 == sscanf(line," %63[^= ] = %191[^\n]",tag,value)) {
+ /* foo = bar */
+ if (NULL == e) {
+ fprintf(stderr,"%s:%d: error: no section\n",filename,nr);
+ } else {
+ cfg_set_entry(e,tag,value);
+ }
+ } else {
+ /* Huh ? */
+ fprintf(stderr,"%s:%d: syntax error\n",filename,nr);
+ }
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+char**
+cfg_list_sections()
+{
+ return c->sec_names;
+}
+
+char**
+cfg_list_entries(char *name)
+{
+ int i;
+
+ for (i = 0; i < c->sec_count; i++)
+ if (0 == strcasecmp(c->sec_names[i],name))
+ return c->sec_entries[i]->ent_names;
+ return NULL;
+}
+
+char*
+cfg_get_str(char *sec, char *ent)
+{
+ struct CFG_ENTRIES* e = NULL;
+ char *v = NULL;
+ int i;
+
+ for (i = 0; i < c->sec_count; i++)
+ if (0 == strcasecmp(c->sec_names[i],sec))
+ e = c->sec_entries[i];
+ if (NULL == e)
+ return NULL;
+ for (i = 0; i < e->ent_count; i++)
+ if (0 == strcasecmp(e->ent_names[i],ent)) {
+ v = e->ent_values[i];
+ e->ent_seen[i]++;
+ }
+ return v;
+}
+
+int
+cfg_get_int(char *sec, char *ent)
+{
+ char *val;
+
+ val = cfg_get_str(sec,ent);
+ if (NULL == val)
+ return -1;
+ return atoi(val);
+}
+
+int
+cfg_get_signed_int(char *sec, char *ent)
+{
+ char *val;
+
+ val = cfg_get_str(sec,ent);
+ if (NULL == val)
+ return 0;
+ return atoi(val);
+}
+
+float
+cfg_get_float(char *sec, char *ent)
+{
+ char *val;
+
+ val = cfg_get_str(sec,ent);
+ if (NULL == val)
+ return -1;
+ return atof(val);
+}
diff --git a/common/parseconfig.h b/common/parseconfig.h
new file mode 100644
index 0000000..e3b609d
--- /dev/null
+++ b/common/parseconfig.h
@@ -0,0 +1,7 @@
+int cfg_parse_file(char *filename);
+char** cfg_list_sections(void);
+char** cfg_list_entries(char *name);
+char* cfg_get_str(char *sec, char *ent);
+int cfg_get_int(char *sec, char *ent);
+int cfg_get_signed_int(char *sec, char *ent);
+float cfg_get_float(char *sec, char *ent);
diff --git a/common/sound.c b/common/sound.c
new file mode 100644
index 0000000..490d3f2
--- /dev/null
+++ b/common/sound.c
@@ -0,0 +1,68 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "grab-ng.h"
+#include "sound.h"
+
+void
+oss_levels(struct ng_audio_buf *buf, int *left, int *right)
+{
+ int lmax,rmax,i,level;
+ signed char *s = buf->data;
+ unsigned char *u = buf->data;
+
+ lmax = 0;
+ rmax = 0;
+ switch (buf->fmt.fmtid) {
+ case AUDIO_U8_MONO:
+ i = 0;
+ while (i < buf->size) {
+ level = abs((int)u[i++] - 128);
+ if (lmax < level)
+ lmax = level, rmax = level;
+ }
+ break;
+ case AUDIO_U8_STEREO:
+ i = 0;
+ while (i < buf->size) {
+ level = abs((int)u[i++] - 128);
+ if (lmax < level)
+ lmax = level;
+ level = abs((int)u[i++] - 128);
+ if (rmax < level)
+ rmax = level;
+ }
+ break;
+ case AUDIO_S16_BE_MONO:
+ case AUDIO_S16_LE_MONO:
+ i = (AUDIO_S16_BE_MONO == buf->fmt.fmtid) ? 0 : 1;
+ while (i < buf->size) {
+ level = abs((int)s[i]);
+ i += 2;
+ if (lmax < level)
+ lmax = level, rmax = level;
+ }
+ break;
+ case AUDIO_S16_LE_STEREO:
+ case AUDIO_S16_BE_STEREO:
+ i = (AUDIO_S16_BE_STEREO == buf->fmt.fmtid) ? 0 : 1;
+ while (i < buf->size) {
+ level = abs((int)s[i]);
+ i += 2;
+ if (lmax < level)
+ lmax = level;
+ level = abs((int)s[i]);
+ i += 2;
+ if (rmax < level)
+ rmax = level;
+ }
+ break;
+ }
+ *left = lmax;
+ *right = rmax;
+}
diff --git a/common/sound.h b/common/sound.h
new file mode 100644
index 0000000..7eb767f
--- /dev/null
+++ b/common/sound.h
@@ -0,0 +1,6 @@
+#ifndef _SOUND_H_
+#define _SOUND_H_
+
+void oss_levels(struct ng_audio_buf *buf, int *left, int *right);
+
+#endif /* _SOUND_H_ */
diff --git a/common/webcam.c b/common/webcam.c
new file mode 100644
index 0000000..ab86b1b
--- /dev/null
+++ b/common/webcam.c
@@ -0,0 +1,157 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "grab-ng.h"
+#include "writefile.h"
+#include "webcam.h"
+
+extern int jpeg_quality;
+extern int debug;
+char *webcam;
+
+struct WEBCAM {
+ pthread_mutex_t lock;
+ pthread_cond_t wait;
+ char *filename;
+ struct ng_video_buf *buf;
+};
+
+static void*
+webcam_writer(void *arg)
+{
+ struct WEBCAM *web = arg;
+ int rename,fd,old;
+ char tmpfilename[512];
+ struct ng_video_fmt *fmt;
+
+ if (debug)
+ fprintf(stderr,"webcam_writer start\n");
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,&old);
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&old);
+ pthread_mutex_lock(&web->lock);
+ for (;;) {
+ while (web->buf == NULL) {
+ if (debug)
+ fprintf(stderr,"webcam_writer: waiting for data\n");
+ pthread_cond_wait(&web->wait, &web->lock);
+ }
+ fmt = &web->buf->fmt;
+ if (debug)
+ fprintf(stderr,"webcam_writer: %d %dx%d \n",
+ fmt->fmtid,fmt->width,fmt->height);
+ rename = 1;
+ sprintf(tmpfilename,"%s.$$$",web->filename);
+ switch (fmt->fmtid) {
+ case VIDEO_MJPEG:
+ case VIDEO_JPEG:
+ if (-1 == (fd = open(tmpfilename,O_CREAT|O_WRONLY,0666))) {
+ fprintf(stderr,"open(%s): %s\n",tmpfilename,
+ strerror(errno));
+ goto done;
+ }
+ write(fd,web->buf->data,web->buf->size);
+ close(fd);
+ break;
+#if 0 /* FIXME */
+ case VIDEO_BGR24:
+ data = malloc(web->buf->size);
+ memcpy(data,web->buf->data,web->buf->size);
+ swap_rgb24(data,fmt->width*fmt->height);
+ write_jpeg(tmpfilename,data,jpeg_quality,0);
+ free(data);
+ break;
+#endif
+ case VIDEO_RGB24:
+ write_jpeg(tmpfilename,web->buf,ng_jpeg_quality,0);
+ break;
+ default:
+ fprintf(stderr,"webcam_writer: can't deal with format=%d\n",
+ fmt->fmtid);
+ rename = 0;
+ }
+ if (rename) {
+ unlink(web->filename);
+ if (-1 == link(tmpfilename,web->filename)) {
+ fprintf(stderr,"link(%s,%s): %s\n",
+ tmpfilename,web->filename,strerror(errno));
+ goto done;
+ }
+ unlink(tmpfilename);
+ }
+ free(web->filename);
+ ng_release_video_buf(web->buf);
+ web->buf = NULL;
+ }
+ done:
+ pthread_mutex_unlock(&web->lock);
+ if (debug)
+ fprintf(stderr,"webcam_writer done\n");
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct WEBCAM *web;
+static pthread_t tweb;
+
+void
+webcam_init()
+{
+ web = malloc(sizeof(struct WEBCAM));
+ memset(web,0,sizeof(struct WEBCAM));
+ pthread_mutex_init(&web->lock, NULL);
+ pthread_create(&tweb,NULL,webcam_writer,web);
+ return;
+}
+
+void
+webcam_exit()
+{
+ if (web) {
+ pthread_cancel(tweb);
+ free(web);
+ web = NULL;
+ }
+}
+
+int
+webcam_put(char *filename, struct ng_video_buf *buf)
+{
+ int ret = 0;
+
+ if (NULL == web)
+ webcam_init();
+
+ if (-1 == pthread_mutex_trylock(&web->lock)) {
+ if (debug)
+ fprintf(stderr,"webcam_put: locked\n");
+ return -1;
+ }
+ if (NULL != web->buf) {
+ if (debug)
+ fprintf(stderr,"webcam_put: still has data\n");
+ ret = -1;
+ goto done;
+ }
+
+ web->filename = strdup(filename);
+ web->buf = buf;
+ buf->refcount++;
+ if (debug)
+ fprintf(stderr,"webcam_put: ok\n");
+ pthread_cond_signal(&web->wait);
+
+ done:
+ pthread_mutex_unlock(&web->lock);
+ return ret;
+}
diff --git a/common/webcam.h b/common/webcam.h
new file mode 100644
index 0000000..e3ec4af
--- /dev/null
+++ b/common/webcam.h
@@ -0,0 +1,4 @@
+extern char *webcam;
+int webcam_put(char *filename, struct ng_video_buf *buf);
+void webcam_init(void);
+void webcam_exit(void);

Privacy Policy