]> git.rkrishnan.org Git - pihpsdr.git/commitdiff
Initial stage of manual merge with John's gpiod branch
authorc vw <dl1ycf@darc.de>
Mon, 17 May 2021 07:21:34 +0000 (09:21 +0200)
committerc vw <dl1ycf@darc.de>
Mon, 17 May 2021 07:21:34 +0000 (09:21 +0200)
41 files changed:
Makefile
actions.c [new file with mode: 0644]
actions.h [new file with mode: 0644]
adc.h
alsa_midi.c
alsa_midi.h [new file with mode: 0644]
configure.c
css.c [new file with mode: 0644]
dac.h
discovered.h
discovery.c
encoder_menu.c
ext.c
ext.h
gpio.c
gpio.h
i2c.c
iambic.c
mac_midi.c
main.h
midi.h
midi2.c
midi3.c
midi_menu.c [new file with mode: 0644]
midi_menu.h [new file with mode: 0644]
new_menu.c
radio.c
radio.h
radio_menu.c
rx_panadapter.c
sliders.c
soapy_protocol.c
soapy_protocol.h
switch_menu.c
toolbar.c
toolbar.h
transmitter.c
tx_menu.c
tx_panadapter.c
vfo.c
vfo.h

index 96695a1891b658af38d128d5ec5ca63bc7439101..a596532a543ec22cdba4a85f8d0ada80f08a6164 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,21 +1,35 @@
+# get the OS Name
+UNAME_S := $(shell uname -s)
+
 # Get git commit version and date
 GIT_DATE := $(firstword $(shell git --no-pager show --date=short --format="%ai" --name-only))
 GIT_VERSION := $(shell git describe --abbrev=0 --tags)
 
-# uncomment the line below to include GPIO (For original piHPSDR Controller and Controller2 with i2c)
-GPIO_INCLUDE=GPIO
+# uncomment the following line to force 480x320 screen
+#SMALL_SCREEN_OPTIONS=-D SMALL_SCREEN
 
-# uncomment the line below to include USB Ozy support
-# USBOZY_INCLUDE=USBOZY
+# uncomment the line below to include GPIO
+# For support of:
+#    CONTROLLER1 (Original Controller)
+#    CONTROLLER2_V1 single encoders with MCP23017 switches
+#    CONTROLLER2_V2 dual encoders with MCP23017 switches
+#
+#GPIO_INCLUDE=GPIO
 
 # uncomment the line below to include Pure Signal support
 PURESIGNAL_INCLUDE=PURESIGNAL
 
+# uncomment the line below to include MIDI support
+MIDI_INCLUDE=MIDI
+
+# uncomment the line below to include USB Ozy support
+# USBOZY_INCLUDE=USBOZY
+
 # uncomment the line to below include support local CW keyer
-#LOCALCW_INCLUDE=LOCALCW
+LOCALCW_INCLUDE=LOCALCW
 
 # uncomment the line below for SoapySDR
-#SOAPYSDR_INCLUDE=SOAPYSDR
+SOAPYSDR_INCLUDE=SOAPYSDR
 
 # uncomment the line to below include support for sx1509 i2c expander
 #SX1509_INCLUDE=sx1509
@@ -24,10 +38,8 @@ PURESIGNAL_INCLUDE=PURESIGNAL
 #STEMLAB_DISCOVERY=STEMLAB_DISCOVERY
 
 # uncomment the line below to include support for STEMlab discovery (WITHOUT AVAHI)
-#STEMLAB_DISCOVERY=STEMLAB_DISCOVERY_NOAVAHI
+STEMLAB_DISCOVERY=STEMLAB_DISCOVERY_NOAVAHI
 
-# uncomment the line below to include MIDI support
-#MIDI_INCLUDE=MIDI
 
 # uncomment the line below for various debug facilities
 #DEBUG_OPTION=-D DEBUG
@@ -37,16 +49,23 @@ PURESIGNAL_INCLUDE=PURESIGNAL
 # very early code not included yet
 #SERVER_INCLUDE=SERVER
 
-CC=gcc
-LINK=gcc
+CFLAGS?= -O -Wno-deprecated-declarations
+PKG_CONFIG = pkg-config
 
 ifeq ($(MIDI_INCLUDE),MIDI)
 MIDI_OPTIONS=-D MIDI
-MIDI_SOURCES= alsa_midi.c midi2.c midi3.c
-MIDI_HEADERS= midi.h
-MIDI_OBJS= alsa_midi.o midi2.o midi3.o
+MIDI_HEADERS= midi.h midi_menu.h alsa_midi.h
+ifeq ($(UNAME_S), Darwin)
+MIDI_SOURCES= mac_midi.c midi2.c midi3.c midi_menu.c
+MIDI_OBJS= mac_midi.o midi2.o midi3.o midi_menu.o
+MIDI_LIBS= -framework CoreMIDI -framework Foundation
+endif
+ifeq ($(UNAME_S), Linux)
+MIDI_SOURCES= alsa_midi.c midi2.c midi3.c midi_menu.c
+MIDI_OBJS= alsa_midi.o midi2.o midi3.o midi_menu.o
 MIDI_LIBS= -lasound
 endif
+endif
 
 ifeq ($(PURESIGNAL_INCLUDE),PURESIGNAL)
 PURESIGNAL_OPTIONS=-D PURESIGNAL
@@ -82,7 +101,6 @@ USBOZY_OBJS= \
 ozyio.o
 endif
 
-
 ifeq ($(SOAPYSDR_INCLUDE),SOAPYSDR)
 SOAPYSDR_OPTIONS=-D SOAPYSDR
 SOAPYSDRLIBS=-lSoapySDR
@@ -97,34 +115,30 @@ soapy_discovery.o \
 soapy_protocol.o
 endif
 
-
-ifeq ($(LOCALCW_INCLUDE),LOCALCW)
-LOCALCW_OPTIONS=-D LOCALCW
-LOCALCW_SOURCES= iambic.c
-LOCALCW_HEADERS= iambic.h
-LOCALCW_OBJS   = iambic.o
-endif
-
 ifeq ($(PTT_INCLUDE),PTT)
 PTT_OPTIONS=-D PTT
 endif
 
 ifeq ($(GPIO_INCLUDE),GPIO)
-  GPIO_OPTIONS=-D GPIO
-  GPIO_LIBS=-lwiringPi
-  GPIO_SOURCES= \
+GPIOD_VERSION=$(shell pkg-config --modversion libgpiod)
+ifeq ($(GPIOD_VERSION),1.2)
+GPIOD_OPTIONS=-D OLD_GPIOD
+endif
+GPIO_OPTIONS=-D GPIO
+GPIO_LIBS=-lgpiod -li2c
+GPIO_SOURCES= \
   configure.c \
   i2c.c \
   gpio.c \
   encoder_menu.c \
   switch_menu.c
-  GPIO_HEADERS= \
+GPIO_HEADERS= \
   configure.h \
   i2c.h \
   gpio.h \
   encoder_menu.h \
   switch_menu.h
-  GPIO_OBJS= \
+GPIO_OBJS= \
   configure.o \
   i2c.o \
   gpio.o \
@@ -132,6 +146,13 @@ ifeq ($(GPIO_INCLUDE),GPIO)
   switch_menu.o
 endif
 
+ifeq ($(LOCALCW_INCLUDE),LOCALCW)
+LOCALCW_OPTIONS=-D LOCALCW
+LOCALCW_SOURCES= iambic.c
+LOCALCW_HEADERS= iambic.h
+LOCALCW_OBJS   = iambic.o
+endif
+
 #
 # We have two versions of STEMLAB_DISCOVERY here,
 # the second one has to be used
@@ -140,17 +161,17 @@ endif
 #
 ifeq ($(STEMLAB_DISCOVERY), STEMLAB_DISCOVERY)
 STEMLAB_OPTIONS=-D STEMLAB_DISCOVERY \
-  `pkg-config --cflags avahi-gobject` \
-  `pkg-config --cflags libcurl`
-STEMLAB_LIBS=`pkg-config --libs avahi-gobject` `pkg-config --libs libcurl`
+  $(shell $(PKG_CONFIG) --cflags avahi-gobject) \
+  $(shell $(PKG_CONFIG) --cflags libcurl)
+STEMLAB_LIBS=$(shell $(PKG_CONFIG) --libs avahi-gobject --libs libcurl)
 STEMLAB_SOURCES=stemlab_discovery.c
 STEMLAB_HEADERS=stemlab_discovery.h
 STEMLAB_OBJS=stemlab_discovery.o
 endif
 
 ifeq ($(STEMLAB_DISCOVERY), STEMLAB_DISCOVERY_NOAVAHI)
-STEMLAB_OPTIONS=-D STEMLAB_DISCOVERY -D NO_AVAHI `pkg-config --cflags libcurl`
-STEMLAB_LIBS=`pkg-config --libs libcurl`
+STEMLAB_OPTIONS=-D STEMLAB_DISCOVERY -D NO_AVAHI $(shell $(PKG_CONFIG) --cflags libcurl)
+STEMLAB_LIBS=$(shell $(PKG_CONFIG) --libs libcurl)
 STEMLAB_SOURCES=stemlab_discovery.c
 STEMLAB_HEADERS=stemlab_discovery.h
 STEMLAB_OBJS=stemlab_discovery.o
@@ -166,21 +187,38 @@ SERVER_OBJS= \
 client_server.o server_menu.o
 endif
 
-GTKINCLUDES=`pkg-config --cflags gtk+-3.0`
-GTKLIBS=`pkg-config --libs gtk+-3.0`
+GTKINCLUDES=$(shell $(PKG_CONFIG) --cflags gtk+-3.0)
+GTKLIBS=$(shell $(PKG_CONFIG) --libs gtk+-3.0)
 
-AUDIO_LIBS=-lasound
-#AUDIO_LIBS=-lsoundio
+ifeq ($(UNAME_S), Linux)
+#AUDIO_LIBS=-lpulse-simple -lpulse -lpulse-mainloop-glib
+#AUDIO_SOURCES=pulseaudio.c
+#AUDIO_OBJS=pulseaudio.o
+AUDIO_LIBS=
+AUDIO_SOURCES=audio.c
+AUDIO_OBJS=audio.o
+endif
+ifeq ($(UNAME_S), Darwin)
+AUDIO_OPTIONS=-DPORTAUDIO $(shell $(PKG_CONFIG) --cflags portaudio-2.0)
+AUDIO_LIBS=$(shell $(PKG_CONFIG) --libs portaudio-2.0)
+AUDIO_SOURCES=portaudio.c
+AUDIO_OBJS=portaudio.o
+endif
 
-CFLAGS=        -g -Wno-deprecated-declarations -O3
-OPTIONS=$(MIDI_OPTIONS) $(PURESIGNAL_OPTIONS) $(REMOTE_OPTIONS) $(USBOZY_OPTIONS) \
-       $(GPIO_OPTIONS) $(SOAPYSDR_OPTIONS) $(LOCALCW_OPTIONS) \
-       $(STEMLAB_OPTIONS) \
-        $(PTT_OPTIONS) \
-       $(SERVER_OPTIONS) \
+ifeq ($(UNAME_S), Linux)
+SYSLIBS=-lrt
+endif
+ifeq ($(UNAME_S), Darwin)
+SYSLIBS=-framework IOkit
+endif
+
+OPTIONS=$(SMALL_SCREEN_OPTIONS) $(MIDI_OPTIONS) $(PURESIGNAL_OPTIONS) $(REMOTE_OPTIONS) $(USBOZY_OPTIONS) \
+       $(GPIO_OPTIONS) $(GPIOD_OPTIONS)  $(SOAPYSDR_OPTIONS) $(LOCALCW_OPTIONS) \
+       $(STEMLAB_OPTIONS) $(PTT_OPTIONES) $(SERVER_OPTIONS) $(AUDIO_OPTIONS) \
        -D GIT_DATE='"$(GIT_DATE)"' -D GIT_VERSION='"$(GIT_VERSION)"' $(DEBUG_OPTION)
 
-LIBS=-lrt -lm -lwdsp -lpthread $(AUDIO_LIBS) $(USBOZY_LIBS) $(GTKLIBS) $(GPIO_LIBS) $(SOAPYSDRLIBS) $(STEMLAB_LIBS) $(MIDI_LIBS)
+LIBS=  -lm -lwdsp -lpthread $(SYSLIBS) $(AUDIO_LIBS) $(USBOZY_LIBS) $(GTKLIBS) \
+               $(GPIO_LIBS) $(SOAPYSDRLIBS) $(STEMLAB_LIBS) $(MIDI_LIBS)
 INCLUDES=$(GTKINCLUDES)
 
 COMPILE=$(CC) $(CFLAGS) $(OPTIONS) $(INCLUDES)
@@ -191,7 +229,7 @@ COMPILE=$(CC) $(CFLAGS) $(OPTIONS) $(INCLUDES)
 PROGRAM=pihpsdr
 
 SOURCES= \
-audio.c \
+MacOS.c \
 band.c \
 discovered.c \
 discovery.c \
@@ -255,11 +293,17 @@ led.c \
 ext.c \
 error_handler.c \
 cwramp.c \
-protocols.c
+protocols.c \
+css.c \
+actions.c \
+i2c.c \
+gpio.c \
+encoder_menu.c \
+switch_menu.c
 
 
 HEADERS= \
-audio.h \
+MacOS.h \
 agc.h \
 alex.h \
 band.h \
@@ -324,11 +368,18 @@ memory.h \
 led.h \
 ext.h \
 error_handler.h \
-protocols.h
+protocols.h \
+css.h \
+actions.h \
+configure.h \
+i2c.h \
+gpio.h \
+encoder_menu.h \
+switch_menu.h
 
 
 OBJS= \
-audio.o \
+MacOS.o \
 band.o \
 discovered.o \
 discovery.o \
@@ -392,24 +443,31 @@ led.o \
 ext.o \
 error_handler.o \
 cwramp.o \
-protocols.o
-
-$(PROGRAM):  $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(SOAPYSDR_OBJS) \
-               $(LOCALCW_OBJS) $(GPIO_OBJS) $(PURESIGNAL_OBJS) \
+protocols.o \
+css.o \
+actions.o \
+configure.o \
+i2c.o \
+gpio.o \
+encoder_menu.o \
+switch_menu.o
+
+$(PROGRAM):  $(OBJS) $(AUDIO_OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(SOAPYSDR_OBJS) \
+               $(LOCALCW_OBJS) $(PURESIGNAL_OBJS) \
                $(MIDI_OBJS) $(STEMLAB_OBJS) $(SERVER_OBJS)
-       $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(GPIO_OBJS) \
+       $(CC) -o $(PROGRAM) $(OBJS) $(AUDIO_OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) \
                $(SOAPYSDR_OBJS) $(LOCALCW_OBJS) $(PURESIGNAL_OBJS) \
-               $(MIDI_OBJS) $(STEMLAB_OBJS) $(SERVER_OBJS) $(LIBS)
-
-.PHONY:        all
-all:   prebuild  $(PROGRAM) $(HEADERS) $(USBOZY_HEADERS) $(SOAPYSDR_HEADERS) \
-       $(LOCALCW_HEADERS) $(GPIO_HEADERS) \
-       $(PURESIGNAL_HEADERS) $(MIDI_HEADERS) $(STEMLAB_HEADERS) $(SERVER_HEADERS)\
-       $(SOURCES) \
-       $(USBOZY_SOURCES) $(SOAPYSDR_SOURCES) $(LOCALCW_SOURCE) $(GPIO_SOURCES) \
+               $(MIDI_OBJS) $(STEMLAB_OBJS) $(SERVER_OBJS) $(LIBS) $(LDFLAGS)
+
+.PHONY: all
+all:    prebuild  $(PROGRAM) $(HEADERS) $(AUDIO_HEADERS) $(USBOZY_HEADERS) $(SOAPYSDR_HEADERS) \
+       $(LOCALCW_HEADERS) \
+       $(PURESIGNAL_HEADERS) $(MIDI_HEADERS) $(STEMLAB_HEADERS) $(SERVER_HEADERS) \
+       $(AUDIO_SOURCES) $(SOURCES) \
+       $(USBOZY_SOURCES) $(SOAPYSDR_SOURCES) $(LOCALCW_SOURCE) \
        $(PURESIGNAL_SOURCES) $(MIDI_SOURCES) $(STEMLAB_SOURCES) $(SERVER_SOURCES)
 
-.PHONY:        prebuild
+.PHONY: prebuild
 prebuild:
        rm -f version.o
 
@@ -419,47 +477,47 @@ prebuild:
 # Therefore, correct this here. Furthermore, we can add additional options to CPP
 # in the variable CPPOPTIONS
 #
-CPPOPTIONS= --enable=all --suppress=shadowVariable --suppress=variableScope
+CPPOPTIONS= --enable=all --suppress=shadowVariable --suppress=variableScope -D__APPLE__
 CPPINCLUDES:=$(shell echo $(INCLUDES) | sed -e "s/-pthread / /" )
 
 .PHONY:        cppcheck
 cppcheck:
        cppcheck $(CPPOPTIONS) $(OPTIONS) $(CPPINCLUDES) $(SOURCES) $(REMOTE_SOURCES) \
-       $(USBOZY_SOURCES) $(SOAPYSDR_SOURCES) $(GPIO_SOURCES) \
-       $(PURESIGNAL_SOURCES) $(MIDI_SOURCES) $(STEMLAB_SOURCES) $(LOCALCW_SOURCES) \
-       $(SERVER_SOURCES)
+       $(USBOZY_SOURCES) $(SOAPYSDR_SOURCES) $(SERVER_SOURCES) \
+       $(PURESIGNAL_SOURCES) $(MIDI_SOURCES) $(STEMLAB_SOURCES) $(LOCALCW_SOURCES)
 
-.PHONY:        clean
+.PHONY: clean
 clean:
        -rm -f *.o
        -rm -f $(PROGRAM) hpsdrsim
+       -rm -rf $(PROGRAM).app
 
-.PHONY:        install
+.PHONY: install
 install: $(PROGRAM)
-       cp $(PROGRAM) /usr/local/bin
+       cp $(PROGRAM) $(DESTDIR)/usr/local/bin
 
-.PHONY:        release
+.PHONY: release
 release: $(PROGRAM)
        cp $(PROGRAM) release/pihpsdr
        cd release; tar cvf pihpsdr.tar pihpsdr
        cd release; tar cvf pihpsdr-$(GIT_VERSION).tar pihpsdr
 
-.PHONY:        nocontroller
+.PHONY: nocontroller
 nocontroller: clean controller1 $(PROGRAM)
        cp $(PROGRAM) release/pihpsdr
        cd release; tar cvf pihpsdr-nocontroller.$(GIT_VERSION).tar pihpsdr
 
-.PHONY:        controller1
+.PHONY: controller1
 controller1: clean $(PROGRAM)
        cp $(PROGRAM) release/pihpsdr
        cd release; tar cvf pihpsdr-controller1.$(GIT_VERSION).tar pihpsdr
 
-.PHONY:        controller2v1
+.PHONY: controller2v1
 controller2v1: clean $(PROGRAM)
        cp $(PROGRAM) release/pihpsdr
        cd release; tar cvf pihpsdr-controller2-v1.$(GIT_VERSION).tar pihpsdr
 
-.PHONY:        controller2v2
+.PHONY: controller2v2
 controller2v2: clean $(PROGRAM)
        cp $(PROGRAM) release/pihpsdr
        cd release; tar cvf pihpsdr-controller2-v2.$(GIT_VERSION).tar pihpsdr
@@ -474,15 +532,14 @@ controller2v2: clean $(PROGRAM)
 #
 #############################################################################
 
-hpsdrsim.o:    hpsdrsim.c hpsdrsim.h
-       $(CC) -c -O -DALSASOUND hpsdrsim.c
-
+hpsdrsim.o:     hpsdrsim.c  hpsdrsim.h
+       $(CC) -c -O $(AUDIO_OPTIONS) hpsdrsim.c
+       
 newhpsdrsim.o: newhpsdrsim.c hpsdrsim.h
        $(CC) -c -O newhpsdrsim.c
 
-hpsdrsim:      hpsdrsim.o newhpsdrsim.o
-       $(LINK) -o hpsdrsim hpsdrsim.o newhpsdrsim.o -lasound -lm -lpthread
-
+hpsdrsim:       hpsdrsim.o newhpsdrsim.o
+       $(CC) -o hpsdrsim $(AUDIO_LIBS) hpsdrsim.o newhpsdrsim.o -lportaudio -lm -lpthread
 
 debian:
        cp $(PROGRAM) pkg/pihpsdr/usr/local/bin
@@ -492,3 +549,48 @@ debian:
        cp release/pihpsdr/pihpsdr.desktop pkg/pihpsdr/usr/share/applications
        cd pkg; dpkg-deb --build pihpsdr
 
+#############################################################################
+#
+# This is for MacOS "app" creation ONLY
+#
+#       The piHPSDR working directory is
+#      $HOME -> Application Support -> piHPSDR
+#
+#       That is the directory where the WDSP wisdom file (created upon first
+#       start of piHPSDR) but also the radio settings and the midi.props file
+#       are stored.
+#
+#       ONLY the wdsp library is bundled with the app, all others, including
+#       the SoapySDR support modules, must be installed separatedly.
+#
+#############################################################################
+
+.PHONY: app
+app:   $(OBJS) $(REMOTE_OBJS) \
+               $(USBOZY_OBJS)  $(SOAPYSDR_OBJS) \
+               $(LOCALCW_OBJS) $(PURESIGNAL_OBJS) \
+               $(MIDI_OBJS) $(STEMLAB_OBJS) $(SERVER_OBJS)
+       $(CC)   -headerpad_max_install_names -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) \
+               $(USBOZY_OBJS)  $(SOAPYSDR_OBJS) \
+               $(LOCALCW_OBJS) $(PURESIGNAL_OBJS) \
+               $(MIDI_OBJS) $(STEMLAB_OBJS) $(SERVER_OBJS) $(LIBS) $(LDFLAGS)
+       @rm -rf pihpsdr.app
+       @mkdir -p pihpsdr.app/Contents/MacOS
+       @mkdir -p pihpsdr.app/Contents/Frameworks
+       @mkdir -p pihpsdr.app/Contents/Resources
+       @cp pihpsdr pihpsdr.app/Contents/MacOS/pihpsdr
+       @cp MacOS/PkgInfo pihpsdr.app/Contents
+       @cp MacOS/Info.plist pihpsdr.app/Contents
+       @cp MacOS/hpsdr.icns pihpsdr.app/Contents/Resources/hpsdr.icns
+       @cp MacOS/hpsdr.png pihpsdr.app/Contents/Resources
+#
+#      Copy the WDSP library into the executable
+#
+       @lib=`otool -L pihpsdr.app/Contents/MacOS/pihpsdr | grep libwdsp | sed -e "s/ (.*//" | sed -e 's/       //'`; \
+        libfn=`basename $$lib`; \
+        cp "$$lib" "pihpsdr.app/Contents/Frameworks/$$libfn"; \
+        chmod u+w "pihpsdr.app/Contents/Frameworks/$$libfn"; \
+        install_name_tool -id "@executable_path/../Frameworks/$$libfn" "pihpsdr.app/Contents/Frameworks/$$libfn"; \
+        install_name_tool -change "$$lib" "@executable_path/../Frameworks/$$libfn" pihpsdr.app/Contents/MacOS/pihpsdr
+#
+#############################################################################
diff --git a/actions.c b/actions.c
new file mode 100644 (file)
index 0000000..4614b72
--- /dev/null
+++ b/actions.c
@@ -0,0 +1,931 @@
+#include <gtk/gtk.h>
+
+#include "main.h"
+#include "discovery.h"
+#include "receiver.h"
+#include "sliders.h"
+#include "band_menu.h"
+#include "diversity_menu.h"
+#include "vfo.h"
+#include "radio.h"
+#include "radio_menu.h"
+#include "new_menu.h"
+#include "new_protocol.h"
+#ifdef PURESIGNAL
+#include "ps_menu.h"
+#endif
+#include "agc.h"
+#include "filter.h"
+#include "mode.h"
+#include "band.h"
+#include "bandstack.h"
+#include "noise_menu.h"
+#include "wdsp.h"
+#ifdef CLIENT_SERVER
+#include "client_server.h"
+#endif
+#include "ext.h"
+#include "zoompan.h"
+#include "actions.h"
+#include "gpio.h"
+#include "toolbar.h"
+
+char *encoder_string[ENCODER_ACTIONS] = {
+  "NO ACTION",
+  "AF GAIN",
+  "AF GAIN RX1",
+  "AF GAIN RX2",
+  "AGC GAIN",
+  "AGC GAIN RX1",
+  "AGC GAIN RX2",
+  "ATTENUATION",
+  "COMP",
+  "CW FREQUENCY",
+  "CW SPEED",
+  "DIVERSITY GAIN",
+  "DIVERSITY GAIN (coarse)",
+  "DIVERSITY GAIN (fine)",
+  "DIVERSITY PHASE",
+  "DIVERSITY PHASE (coarse)",
+  "DIVERSITY PHASE (fine)",
+  "DRIVE",
+  "IF SHIFT",
+  "IF SHIFT RX1",
+  "IF SHIFT RX2",
+  "IF WIDTH",
+  "IF WIDTH RX1",
+  "IF WIDTH RX2",
+  "MIC GAIN",
+  "PAN",
+  "PANADAPTER HIGH",
+  "PANADAPTER LOW",
+  "PANADAPTER STEP",
+  "RF GAIN",
+  "RF GAIN RX1",
+  "RF GAIN RX2",
+  "RIT",
+  "RIT RX1",
+  "RIT RX2",
+  "SQUELCH",
+  "SQUELCH RX1",
+  "SQUELCH RX2",
+  "TUNE DRIVE",
+  "VFO",
+  "WATERFALL HIGH",
+  "WATERFALL LOW",
+  "XIT",
+  "ZOOM"
+};
+
+char *sw_string[SWITCH_ACTIONS] = {
+  "",
+  "A TO B",
+  "A SWAP B",
+  "AGC +",
+  "ANF",
+  "B TO A",
+  "BAND -",
+  "BAND +",
+  "BSTACK -",
+  "BSTACK +",
+  "CTUN",
+  "DIV",
+  "DUPLEX",
+  "FILTER -",
+  "FILTER +",
+  "FUNCT",
+  "LOCK",
+  "AGC",
+  "BAND",
+  "BSTACK",
+  "DIV",
+  "FILTER",
+  "FREQUENCY",
+  "MEMORY",
+  "MODE",
+  "NOISE",
+  "PS",
+  "MODE -",
+  "MODE +",
+  "MOX",
+  "MUTE",
+  "NB",
+  "NR",
+  "PAN -",
+  "PAN +",
+  "PS",
+  "RIT",
+  "RIT CL",
+  "RIT -",
+  "RIT +",
+  "RSAT",
+  "SAT",
+  "SNB",
+  "SPLIT",
+  "TUN",
+  "TUNE FULL",
+  "TUNE MEM",
+  "TWO TONE",
+  "VFOSTEP +",
+  "VFOSTEP -",
+  "XIT",
+  "XIT CL",
+  "XIT -",
+  "XIT +",
+  "ZOOM -",
+  "ZOOM +"
+};
+
+char *sw_cap_string[SWITCH_ACTIONS] = {
+  "",
+  "A>B",
+  "A<>B",
+  "AGC",
+  "ANF",
+  "B>A",
+  "BAND -",
+  "BAND +",
+  "BST-",
+  "BST+",
+  "CTUN",
+  "DIV",
+  "DUP",
+  "FILT -",
+  "FILT +",
+  "FUNC",
+  "LOCK",
+  "AGC",
+  "BAND",
+  "BSTACK",
+  "DIV",
+  "FILTER",
+  "FREQ",
+  "MEM",
+  "MODE",
+  "NOISE",
+  "PS",
+  "MODE -",
+  "MODE +",
+  "MOX",
+  "MUTE",
+  "NB",
+  "NR",
+  "PAN-",
+  "PAN+",
+  "PS",
+  "RIT",
+  "RIT CL",
+  "RIT -",
+  "RIT +",
+  "RSAT",
+  "SAT",
+  "SNB",
+  "SPLIT",
+  "TUNE",
+  "TUN-F",
+  "TUN-M",
+  "2TONE",
+  "STEP+",
+  "STEP-",
+  "XIT",
+  "XIT CL",
+  "XIT -",
+  "XIT +",
+  "ZOOM-",
+  "ZOOM+",
+};
+
+
+int encoder_action(void *data) {
+  ENCODER_ACTION *a=(ENCODER_ACTION *)data;
+  double value;
+  int mode;
+  int id;
+  FILTER * band_filters=filters[vfo[active_receiver->id].mode];
+  FILTER *band_filter;
+  FILTER *filter;
+  int new_val;
+
+  switch(a->action) {
+    case ENCODER_VFO:
+      vfo_step(a->val);
+      break;
+    case ENCODER_AF_GAIN:
+      value=active_receiver->volume;
+      value+=a->val/100.0;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>1.0) {
+        value=1.0;
+      }
+      set_af_gain(active_receiver->id,value);
+      break;
+    case ENCODER_AF_GAIN_RX1:
+      value=receiver[0]->volume;
+      value+=a->val/100.0;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>1.0) {
+        value=1.0;
+      }
+      set_af_gain(0,value);
+      break;
+    case ENCODER_AF_GAIN_RX2:
+      value=receiver[1]->volume;
+      value+=a->val/100.0;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>1.0) {
+        value=1.0;
+      }
+      set_af_gain(1,value);
+      break;
+    case ENCODER_RF_GAIN:
+      //value=active_receiver->gain;
+      value=adc[active_receiver->id].gain;
+      value+=a->val;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>100.0) {
+        value=100.0;
+      }
+      set_rf_gain(active_receiver->id,value);
+      break;
+    case ENCODER_RF_GAIN_RX1:
+      //value=receiver[0]->rf_gain;
+      value=adc[receiver[0]->id].gain;
+      value+=a->val;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>100.0) {
+        value=100.0;
+      }
+      set_rf_gain(0,value);
+      break;
+    case ENCODER_RF_GAIN_RX2:
+      //value=receiver[1]->rf_gain;
+      value=adc[receiver[1]->id].gain;
+      value+=a->val;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>71.0) {
+        value=71.0;
+      }
+      set_rf_gain(1,value);
+      break;
+    case ENCODER_AGC_GAIN:
+      value=active_receiver->agc_gain;
+      value+=a->val;
+      if(value<-20.0) {
+        value=-20.0;
+      } else if(value>120.0) {
+        value=120.0;
+      }
+      set_agc_gain(active_receiver->id,value);
+      break;
+    case ENCODER_AGC_GAIN_RX1:
+      value=receiver[0]->agc_gain;
+      value+=a->val;
+      if(value<-20.0) {
+        value=-20.0;
+      } else if(value>120.0) {
+        value=120.0;
+      }
+      set_agc_gain(0,value);
+      break;
+    case ENCODER_AGC_GAIN_RX2:
+      value=receiver[1]->agc_gain;
+      value+=a->val;
+      if(value<-20.0) {
+        value=-20.0;
+      } else if(value>120.0) {
+        value=120.0;
+      }
+      set_agc_gain(1,value);
+      break;
+    case ENCODER_IF_WIDTH:
+      filter_width_changed(active_receiver->id,a->val);
+      break;
+    case ENCODER_IF_WIDTH_RX1:
+      filter_width_changed(0,a->val);
+      break;
+    case ENCODER_IF_WIDTH_RX2:
+      filter_width_changed(1,a->val);
+      break;
+    case ENCODER_IF_SHIFT:
+      filter_shift_changed(active_receiver->id,a->val);
+      break;
+    case ENCODER_IF_SHIFT_RX1:
+      filter_shift_changed(0,a->val);
+      break;
+    case ENCODER_IF_SHIFT_RX2:
+      filter_shift_changed(1,a->val);
+      break;
+    case ENCODER_ATTENUATION:
+      value=(double)adc[active_receiver->adc].attenuation;
+      value+=(double)a->val;
+      if(have_rx_gain) {
+        if(value<-12.0) {
+          value=-12.0;
+        } else if(value>48.0) {
+          value=48.0;
+        }
+      } else {
+        if(value<0.0) {
+          value=0.0;
+        } else if (value>31.0) {
+          value=31.0;
+        }
+      }
+      set_attenuation_value(value);
+      break;
+    case ENCODER_MIC_GAIN:
+      value=mic_gain;
+      value+=(double)a->val;
+      if(value<-12.0) {
+        value=-12.0;
+      } else if(value>50.0) {
+        value=50.0;
+      }
+      set_mic_gain(value);
+      break;
+    case ENCODER_DRIVE:
+      value=getDrive();
+      value+=(double)a->val;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>drive_max) {
+        value=drive_max;
+      }
+      set_drive(value);
+      break;
+    case ENCODER_RIT:
+      vfo_rit(active_receiver->id,a->val);
+      break;
+    case ENCODER_RIT_RX1:
+      vfo_rit(receiver[0]->id,a->val);
+      break;
+    case ENCODER_RIT_RX2:
+      vfo_rit(receiver[1]->id,a->val);
+      break;
+    case ENCODER_XIT:
+      value=(double)transmitter->xit;
+      value+=(double)(a->val*rit_increment);
+      if(value<-10000.0) {
+        value=-10000.0;
+      } else if(value>10000.0) {
+        value=10000.0;
+      }
+      transmitter->xit=(int)value;
+      if(protocol==NEW_PROTOCOL) {
+        schedule_high_priority();
+      }
+      g_idle_add(ext_vfo_update,NULL);
+      break;
+    case ENCODER_CW_SPEED:
+      value=(double)cw_keyer_speed;
+      value+=(double)a->val;
+      if(value<1.0) {
+        value=1.0;
+      } else if(value>60.0) {
+        value=60.0;
+      }
+      cw_keyer_speed=(int)value;
+      g_idle_add(ext_vfo_update,NULL);
+      break;
+    case ENCODER_CW_FREQUENCY:
+      value=(double)cw_keyer_sidetone_frequency;
+      value+=(double)a->val;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>1000.0) {
+        value=1000.0;
+      }
+      cw_keyer_sidetone_frequency=(int)value;
+      g_idle_add(ext_vfo_update,NULL);
+      break;
+    case ENCODER_PANADAPTER_HIGH:
+      value=(double)active_receiver->panadapter_high;
+      value+=(double)a->val;
+      active_receiver->panadapter_high=(int)value;
+      break;
+    case ENCODER_PANADAPTER_LOW:
+      value=(double)active_receiver->panadapter_low;
+      value+=(double)a->val;
+      active_receiver->panadapter_low=(int)value;
+      break;
+    case ENCODER_PANADAPTER_STEP:
+      value=(double)active_receiver->panadapter_step;
+      value+=(double)a->val;
+      active_receiver->panadapter_step=(int)value;
+      break;
+    case ENCODER_WATERFALL_HIGH:
+      value=(double)active_receiver->waterfall_high;
+      value+=(double)a->val;
+      active_receiver->waterfall_high=(int)value;
+      break;
+    case ENCODER_WATERFALL_LOW:
+      value=(double)active_receiver->waterfall_low;
+      value+=(double)a->val;
+      active_receiver->waterfall_low=(int)value;
+      break;
+    case ENCODER_SQUELCH:
+      value=active_receiver->squelch;
+      value+=(double)a->val;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>100.0) {
+        value=100.0;
+      }
+      active_receiver->squelch=value;
+      set_squelch(active_receiver);
+      break;
+    case ENCODER_SQUELCH_RX1:
+      value=receiver[0]->squelch;
+      value+=(double)a->val;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>100.0) {
+        value=100.0;
+      }
+      receiver[0]->squelch=value;
+      set_squelch(receiver[0]);
+      break;
+    case ENCODER_SQUELCH_RX2:
+      value=receiver[1]->squelch;
+      value+=(double)a->val;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>100.0) {
+        value=100.0;
+      }
+      receiver[1]->squelch=value;
+      set_squelch(receiver[1]);
+      break;
+    case ENCODER_COMP:
+      value=(double)transmitter->compressor_level;
+      value+=(double)a->val;
+      if(value<0.0) {
+        value=0.0;
+      } else if(value>20.0) {
+        value=20.0;
+      }
+      transmitter->compressor_level=(int)value;
+      set_compression(transmitter);
+      break;
+    case ENCODER_DIVERSITY_GAIN:
+      update_diversity_gain((double)a->val * 0.5);
+      break;
+    case ENCODER_DIVERSITY_GAIN_COARSE:
+      update_diversity_gain((double)a->val * 2.5);
+      break;
+    case ENCODER_DIVERSITY_GAIN_FINE:
+      update_diversity_gain((double)a->val * 0.1);
+      break;
+    case ENCODER_DIVERSITY_PHASE:
+      update_diversity_phase((double)a->val* 0.5);
+      break;
+    case ENCODER_DIVERSITY_PHASE_COARSE:
+      update_diversity_phase((double)a->val*2.5);
+      break;
+    case ENCODER_DIVERSITY_PHASE_FINE:
+      update_diversity_phase((double)a->val*0.1);
+      break;
+    case ENCODER_ZOOM:
+      update_zoom((double)a->val);
+      break;
+    case ENCODER_PAN:
+      update_pan((double)a->val*100);
+      break;
+  }
+  g_free(data);
+  return 0;
+}
+
+int switch_action(void *data) {
+  int i;
+  SWITCH_ACTION *a=(SWITCH_ACTION *)data;
+  if(a->state==PRESSED) {
+    switch(a->action) {
+      case FUNCTION:
+       switch(controller) {
+          case NO_CONTROLLER:
+          case CONTROLLER1:
+            function++;
+            if(function>=MAX_FUNCTIONS) {
+              function=0;
+            }
+            toolbar_switches=switches_controller1[function];
+            switches=switches_controller1[function];
+            update_toolbar_labels();
+           break;
+         case CONTROLLER2_V1:
+         case CONTROLLER2_V2:
+            function++;
+            if(function>=MAX_FUNCTIONS) {
+              function=0;
+            }
+            toolbar_switches=switches_controller1[function];
+            update_toolbar_labels();
+           break;
+        }
+        break;
+      case TUNE:
+       if(getMox()==1) {
+          setMox(0);
+        }
+        if(getTune()==0) {
+          if(canTransmit() || tx_out_of_band) {
+            setTune(1);
+          } else {
+            transmitter_set_out_of_band(transmitter);
+          }
+        } else {
+          setTune(0);
+        }
+        g_idle_add(ext_vfo_update,NULL);
+        break;
+      case MOX:
+        if(getTune()==1) {
+          setTune(0);
+        }
+        if(getMox()==0) {
+          if(canTransmit() || tx_out_of_band) {
+            setMox(1);
+          } else {
+            transmitter_set_out_of_band(transmitter);
+          }
+        } else {
+          setMox(0);
+        }
+        g_idle_add(ext_vfo_update,NULL);
+        break;
+      case PS:
+#ifdef PURESIGNAL
+        if(can_transmit) {
+          if(transmitter->puresignal==0) {
+            tx_set_ps(transmitter,1);
+          } else {
+            tx_set_ps(transmitter,0);
+          }
+        }
+#endif
+        break;
+      case TWO_TONE:
+        if(can_transmit) {
+          int state=transmitter->twotone?0:1;
+          tx_set_twotone(transmitter,state);
+        }
+        break;
+      case VFOSTEP_PLUS:
+        for(i=0;i<STEPS;i++) {
+          if(steps[i]==step) break;
+        }
+        if(i>=STEPS) i=0;
+       i++;
+       if(i>=STEPS) i=0;
+        step=steps[i];
+        g_idle_add(ext_vfo_update, NULL);
+       break;
+      case VFOSTEP_MINUS:
+        for(i=0;i<STEPS;i++) {
+          if(steps[i]==step) break;
+        }
+        if(i>=STEPS) i=0;
+       i--;
+       if(i<0) i=STEPS-1;
+        step=steps[i];
+        g_idle_add(ext_vfo_update, NULL);
+       break;
+      case NR:
+        if(active_receiver->nr==0 && active_receiver->nr2==0) {
+          active_receiver->nr=1;
+          active_receiver->nr2=0;
+          mode_settings[vfo[active_receiver->id].mode].nr=1;
+          mode_settings[vfo[active_receiver->id].mode].nr2=0;
+        } else if(active_receiver->nr==1 && active_receiver->nr2==0) {
+          active_receiver->nr=0;
+          active_receiver->nr2=1;
+          mode_settings[vfo[active_receiver->id].mode].nr=0;
+          mode_settings[vfo[active_receiver->id].mode].nr2=1;
+        } else if(active_receiver->nr==0 && active_receiver->nr2==1) {
+          active_receiver->nr=0;
+          active_receiver->nr2=0;
+          mode_settings[vfo[active_receiver->id].mode].nr=0;
+          mode_settings[vfo[active_receiver->id].mode].nr2=0;
+        }
+        update_noise();
+        break;
+      case NB:
+        if(active_receiver->nb==0 && active_receiver->nb2==0) {
+          active_receiver->nb=1;
+          active_receiver->nb2=0;
+          mode_settings[vfo[active_receiver->id].mode].nb=1;
+          mode_settings[vfo[active_receiver->id].mode].nb2=0;
+        } else if(active_receiver->nb==1 && active_receiver->nb2==0) {
+          active_receiver->nb=0;
+          active_receiver->nb2=1;
+          mode_settings[vfo[active_receiver->id].mode].nb=0;
+          mode_settings[vfo[active_receiver->id].mode].nb2=1;
+        } else if(active_receiver->nb==0 && active_receiver->nb2==1) {
+          active_receiver->nb=0;
+          active_receiver->nb2=0;
+          mode_settings[vfo[active_receiver->id].mode].nb=0;
+          mode_settings[vfo[active_receiver->id].mode].nb2=0;
+        }
+        update_noise();
+        break;
+      case SNB:
+        if(active_receiver->snb==0) {
+          active_receiver->snb=1;
+          mode_settings[vfo[active_receiver->id].mode].snb=1;
+        } else {
+          active_receiver->snb=0;
+          mode_settings[vfo[active_receiver->id].mode].snb=0;
+        }
+        update_noise();
+        break;
+      case RIT:
+        vfo_rit_update(active_receiver->id);
+        break;
+      case RIT_CLEAR:
+        vfo_rit_clear(active_receiver->id);
+        break;
+      case RIT_MINUS:
+       vfo_rit(active_receiver->id,-1);
+        break;
+      case RIT_PLUS:
+       vfo_rit(active_receiver->id,1);
+        break;
+      case XIT:
+        if(can_transmit) {
+          transmitter->xit_enabled=transmitter->xit_enabled==1?0:1;
+          if(protocol==NEW_PROTOCOL) {
+            schedule_high_priority();
+          }
+        }
+        g_idle_add(ext_vfo_update, NULL);
+        break;
+      case XIT_CLEAR:
+        if(can_transmit) {
+          transmitter->xit=0;
+          g_idle_add(ext_vfo_update, NULL);
+        }
+        break;
+      case XIT_MINUS:
+        if(can_transmit) {
+          double value=(double)transmitter->xit;
+          value-=(double)rit_increment;
+          if(value<-10000.0) {
+            value=-10000.0;
+          } else if(value>10000.0) {
+            value=10000.0;
+          }
+          transmitter->xit=(int)value;
+          if(protocol==NEW_PROTOCOL) {
+            schedule_high_priority();
+          }
+          g_idle_add(ext_vfo_update,NULL);
+       }
+       break;
+      case XIT_PLUS:
+        if(can_transmit) {
+          double value=(double)transmitter->xit;
+          value+=(double)rit_increment;
+          if(value<-10000.0) {
+            value=-10000.0;
+          } else if(value>10000.0) {
+            value=10000.0;
+          }
+          transmitter->xit=(int)value;
+          if(protocol==NEW_PROTOCOL) {
+            schedule_high_priority();
+          }
+          g_idle_add(ext_vfo_update,NULL);
+        }
+        break;
+      case BAND_PLUS:
+        {
+        long long frequency_min=radio->frequency_min;
+        long long frequency_max=radio->frequency_max;
+        int b=vfo[active_receiver->id].band;
+        BAND *band;
+        int found=0;
+        while(!found) {
+          b++;
+          if(b>=BANDS+XVTRS) b=0;
+          band=(BAND*)band_get_band(b);
+          if(strlen(band->title)>0) {
+            if(b<BANDS) {
+              if(!(band->frequencyMin==0.0 && band->frequencyMax==0.0)) {
+                if(band->frequencyMin<frequency_min || band->frequencyMax>frequency_max) {
+                  continue;
+                }
+              }
+            }
+            vfo_band_changed(active_receiver->id,b);
+            found=1;
+          }
+        }
+        }
+        break;
+      case BAND_MINUS:
+        {
+        long long frequency_min=radio->frequency_min;
+        long long frequency_max=radio->frequency_max;
+        int b=vfo[active_receiver->id].band;
+        BAND *band;
+        int found=0;
+        while(!found) {
+          b--;
+          if(b<0) b=BANDS+XVTRS-1;
+          band=(BAND*)band_get_band(b);
+          if(strlen(band->title)>0) {
+            if(b<BANDS) {
+              if(band->frequencyMin<frequency_min || band->frequencyMax>frequency_max) {
+                continue;
+              }
+            }
+            vfo_band_changed(active_receiver->id,b);
+            found=1;
+          }
+        }
+        }
+        break;
+      case BANDSTACK_PLUS:
+        {
+        BAND *band=band_get_band(vfo[active_receiver->id].band);
+        BANDSTACK *bandstack=band->bandstack;
+        int b=vfo[active_receiver->id].bandstack+1;
+        if(b>=bandstack->entries) b=0;
+        vfo_bandstack_changed(b);
+        }
+        break;
+      case BANDSTACK_MINUS:
+        {
+        BAND *band=band_get_band(vfo[active_receiver->id].band);
+        BANDSTACK *bandstack=band->bandstack;
+        int b=vfo[active_receiver->id].bandstack-1;
+        if(b<0) b=bandstack->entries-1;;
+        vfo_bandstack_changed(b);
+        }
+        break;
+      case MODE_PLUS:
+        {
+        int mode=vfo[active_receiver->id].mode;
+        mode++;
+        if(mode>=MODES) mode=0;
+        vfo_mode_changed(mode);
+        }
+        break;
+      case MODE_MINUS:
+        {
+        int mode=vfo[active_receiver->id].mode;
+        mode--;
+        if(mode<0) mode=MODES-1;
+        vfo_mode_changed(mode);
+        }
+        break;
+      case FILTER_PLUS:
+        {
+        int f=vfo[active_receiver->id].filter-1;
+        if(f<0) f=FILTERS-1;
+        vfo_filter_changed(f);
+        }
+        break;
+      case FILTER_MINUS:
+        {
+        int f=vfo[active_receiver->id].filter+1;
+        if(f>=FILTERS) f=0;
+        vfo_filter_changed(f);
+        }
+        break;
+      case A_TO_B:
+        vfo_a_to_b();
+        break;
+      case B_TO_A:
+        vfo_b_to_a();
+        break;
+      case A_SWAP_B:
+        vfo_a_swap_b();
+        break;
+      case LOCK:
+#ifdef CLIENT_SERVER
+        if(radio_is_remote) {
+          send_lock(client_socket,locked==1?0:1);
+        } else {
+#endif
+          locked=locked==1?0:1;
+          g_idle_add(ext_vfo_update, NULL);
+#ifdef CLIENT_SERVER
+        }
+#endif
+        break;
+      case CTUN:
+        vfo[active_receiver->id].ctun=!vfo[active_receiver->id].ctun;
+        if(!vfo[active_receiver->id].ctun) {
+          vfo[active_receiver->id].offset=0;
+        }
+        vfo[active_receiver->id].ctun_frequency=vfo[active_receiver->id].frequency;
+        set_offset(receiver[active_receiver->id],vfo[active_receiver->id].offset);
+        g_idle_add(ext_vfo_update, NULL);
+        break;
+      case AGC:
+        active_receiver->agc++;
+        if(active_receiver->agc>+AGC_LAST) {
+          active_receiver->agc=0;
+        }
+        set_agc(active_receiver, active_receiver->agc);
+        g_idle_add(ext_vfo_update, NULL);
+        break;
+      case SPLIT:
+        if(can_transmit) {
+          split=split==1?0:1;
+          tx_set_mode(transmitter,get_tx_mode());
+          g_idle_add(ext_vfo_update, NULL);
+        }
+        break;
+      case DIVERSITY:
+        diversity_enabled=diversity_enabled==1?0:1;
+        if (protocol == NEW_PROTOCOL) {
+          schedule_high_priority();
+          schedule_receive_specific();
+        }
+        g_idle_add(ext_vfo_update, NULL);
+        break;
+      case SAT:
+        switch(sat_mode) {
+          case SAT_NONE:
+            sat_mode=SAT_MODE;
+            break;
+          case SAT_MODE:
+            sat_mode=RSAT_MODE;
+            break;
+          case RSAT_MODE:
+            sat_mode=SAT_NONE;
+            break;
+        }
+        g_idle_add(ext_vfo_update, NULL);
+        break;
+      case MENU_AGC:
+        start_agc();
+        break;
+      case MENU_BAND:
+        start_band();
+        break;
+      case MENU_BANDSTACK:
+        start_bandstack();
+        break;
+      case MENU_MODE:
+        start_mode();
+        break;
+      case MENU_NOISE:
+        start_noise();
+        break;
+      case MENU_FILTER:
+        start_filter();
+        break;
+      case MENU_FREQUENCY:
+        start_vfo(active_receiver->id);
+        break;
+      case MENU_MEMORY:
+        start_store();
+        break;
+      case MENU_DIVERSITY:
+        start_diversity();
+        break;
+#ifdef PURESIGNAL
+      case MENU_PS:
+        start_ps();
+        break;
+#endif
+      case MUTE:
+        active_receiver->mute_radio=!active_receiver->mute_radio;
+        break;
+      case PAN_MINUS:
+        update_pan(-100.0);
+        break;
+      case PAN_PLUS:
+        update_pan(+100.0);
+        break;
+      case ZOOM_MINUS:
+        update_zoom(-1);
+        break;
+      case ZOOM_PLUS:
+        update_zoom(+1);
+        break;
+      default:
+        g_print("%s: UNKNOWN PRESSED SWITCH ACTION %d\n",__FUNCTION__,a->action);
+        break;
+    }
+  } else if(a->state==RELEASED) {
+    // only switch functions that increment/decrement while pressed
+    switch(a->action) {
+      default:
+        break;
+    }
+  }
+  g_free(data);
+  return 0;
+}
+
diff --git a/actions.h b/actions.h
new file mode 100644 (file)
index 0000000..b48c5b3
--- /dev/null
+++ b/actions.h
@@ -0,0 +1,137 @@
+
+enum {
+  ENCODER_NO_ACTION=0,
+  ENCODER_AF_GAIN,
+  ENCODER_AF_GAIN_RX1,
+  ENCODER_AF_GAIN_RX2,
+  ENCODER_AGC_GAIN,
+  ENCODER_AGC_GAIN_RX1,
+  ENCODER_AGC_GAIN_RX2,
+  ENCODER_ATTENUATION,
+  ENCODER_COMP,
+  ENCODER_CW_FREQUENCY,
+  ENCODER_CW_SPEED,
+  ENCODER_DIVERSITY_GAIN,
+  ENCODER_DIVERSITY_GAIN_COARSE,
+  ENCODER_DIVERSITY_GAIN_FINE,
+  ENCODER_DIVERSITY_PHASE,
+  ENCODER_DIVERSITY_PHASE_COARSE,
+  ENCODER_DIVERSITY_PHASE_FINE,
+  ENCODER_DRIVE,
+  ENCODER_IF_SHIFT,
+  ENCODER_IF_SHIFT_RX1,
+  ENCODER_IF_SHIFT_RX2,
+  ENCODER_IF_WIDTH,
+  ENCODER_IF_WIDTH_RX1,
+  ENCODER_IF_WIDTH_RX2,
+  ENCODER_MIC_GAIN,
+  ENCODER_PAN,
+  ENCODER_PANADAPTER_HIGH,
+  ENCODER_PANADAPTER_LOW,
+  ENCODER_PANADAPTER_STEP,
+  ENCODER_RF_GAIN,
+  ENCODER_RF_GAIN_RX1,
+  ENCODER_RF_GAIN_RX2,
+  ENCODER_RIT,
+  ENCODER_RIT_RX1,
+  ENCODER_RIT_RX2,
+  ENCODER_SQUELCH,
+  ENCODER_SQUELCH_RX1,
+  ENCODER_SQUELCH_RX2,
+  ENCODER_TUNE_DRIVE,
+  ENCODER_VFO,
+  ENCODER_WATERFALL_HIGH,
+  ENCODER_WATERFALL_LOW,
+  ENCODER_XIT,
+  ENCODER_ZOOM,
+  ENCODER_ACTIONS
+};
+
+enum {
+  NO_ACTION=0,
+  A_TO_B,
+  A_SWAP_B,
+  AGC,
+  ANF,
+  B_TO_A,
+  BAND_MINUS,
+  BAND_PLUS,
+  BANDSTACK_MINUS,
+  BANDSTACK_PLUS,
+  CTUN,
+  DIVERSITY,
+  DUPLEX,
+  FILTER_MINUS,
+  FILTER_PLUS,
+  FUNCTION,
+  LOCK,
+  MENU_AGC,
+  MENU_BAND,
+  MENU_BANDSTACK,
+  MENU_DIVERSITY,
+  MENU_FILTER,
+  MENU_FREQUENCY,
+  MENU_MEMORY,
+  MENU_MODE,
+  MENU_NOISE,
+  MENU_PS,
+  MODE_MINUS,
+  MODE_PLUS,
+  MOX,
+  MUTE,
+  NB,
+  NR,
+  PAN_MINUS,
+  PAN_PLUS,
+  PS,
+  RIT,
+  RIT_CLEAR,
+  RIT_MINUS,
+  RIT_PLUS,
+  RSAT,
+  SAT,
+  SNB,
+  SPLIT,
+  TUNE,
+  TUNE_FULL,
+  TUNE_MEMORY,
+  TWO_TONE,
+  VFOSTEP_PLUS,
+  VFOSTEP_MINUS,
+  XIT,
+  XIT_CLEAR,
+  XIT_MINUS,
+  XIT_PLUS,
+  ZOOM_MINUS,
+  ZOOM_PLUS,
+  SWITCH_ACTIONS,
+};
+
+enum {
+  PRESSED,
+  RELEASED
+};
+
+typedef struct _switch_action {
+  gint action;
+  gboolean state;
+} SWITCH_ACTION;
+
+enum {
+  RELATIVE,
+  ABSOLUTE
+};
+
+typedef struct _encoder_action {
+  gint action;
+  gboolean mode;
+  gint val;
+} ENCODER_ACTION;
+
+extern char *encoder_string[ENCODER_ACTIONS];
+extern char *sw_string[SWITCH_ACTIONS];
+extern char *sw_cap_string[SWITCH_ACTIONS];
+
+extern int encoder_action(void *data);
+extern int switch_action(void *data);
+
diff --git a/adc.h b/adc.h
index c1ab51d36a96c8c4547a21651b2c939a6d35daeb..f0a14b4f14ee96360c2ef263bdf2f989829a9a9a 100644 (file)
--- a/adc.h
+++ b/adc.h
@@ -65,8 +65,10 @@ typedef struct _adc {
   gboolean preamp;
   gint attenuation;
   gboolean enable_step_attenuation;
+  gdouble gain;
+  gdouble min_gain;
+  gdouble max_gain;
 #ifdef SOAPYSDR
-  gint *rx_gain;
   gboolean agc;
 #endif
 } ADC;
index bf124202707b0b796d7329070adee56d322931a3..d05ad69176128ac5202fd56f68b7a5564fbd8a90 100644 (file)
  *       program amidi.c in alsautils.
  */
 
+#include <gtk/gtk.h>
 #include "midi.h"
+#include "midi_menu.h"
+#include "alsa_midi.h"
 
 #ifndef __APPLE__
 
 #include <pthread.h>
 #include <alsa/asoundlib.h>
 
-static pthread_t midi_thread_id;
+MIDI_DEVICE midi_devices[MAX_MIDI_DEVICES];
+int n_midi_devices;
+
+//
+// The following must not reside in midi_devices since it
+// needs special #includes
+//
+static pthread_t midi_thread_id[MAX_MIDI_DEVICES];
+static char *midi_port[MAX_MIDI_DEVICES];
+static snd_rawmidi_t *midi_input[MAX_MIDI_DEVICES];
+
 static void* midi_thread(void *);
 
 static enum {
@@ -41,9 +54,18 @@ static enum {
        CMD_PITCH,
 } command;
 
-static void *midi_thread(void *data) {
+static gboolean configure=FALSE;
+
+void configure_midi_device(gboolean state) {
+  configure=state;
+}
+
+static void *midi_thread(void *arg) {
+    int index = (int) arg;
+    snd_rawmidi_t *input=midi_input[index];
+    char *port=midi_port[index];
+
     int ret;
-    snd_rawmidi_t *input;
     int npfds;
     struct pollfd *pfds;
     unsigned char buf[32];
@@ -51,20 +73,15 @@ static void *midi_thread(void *data) {
     unsigned short revents;
     int i;
     int chan,arg1,arg2;
-    char *portname = (char *) data;
-
-    if ((ret = snd_rawmidi_open(&input, NULL, portname, SND_RAWMIDI_NONBLOCK)) < 0) {
-        fprintf(stderr,"cannot open port \"%s\": %s\n", portname, snd_strerror(ret));
-        free(portname);
-        return NULL;
-    }
-    snd_rawmidi_read(input, NULL, 0); /* trigger reading */
+    
+    
 
     npfds = snd_rawmidi_poll_descriptors_count(input);
     pfds = alloca(npfds * sizeof(struct pollfd));
     snd_rawmidi_poll_descriptors(input, pfds, npfds);
     for (;;) {
        ret = poll(pfds, npfds, 250);
+        if (!midi_devices[index].active) break;
        if (ret < 0) {
             fprintf(stderr,"poll failed: %s\n", strerror(errno));
            // Do not give up, but also do not fire too rapidly
@@ -81,7 +98,7 @@ static void *midi_thread(void *data) {
        ret = snd_rawmidi_read(input, buf, 64);
         if (ret == 0) continue;
         if (ret < 0) {
-            fprintf(stderr,"cannot read from port \"%s\": %s\n", portname, snd_strerror(ret));
+            fprintf(stderr,"cannot read from port \"%s\": %s\n", port, snd_strerror(ret));
             continue;
         }
         // process bytes in buffer. Since they no not necessarily form complete messages
@@ -129,19 +146,39 @@ static void *midi_thread(void *data) {
                           // messages with velocity == 0 when releasing
                           // a push-button
                           if (arg2 == 0) {
-                            NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                             if(configure) {
+                              NewMidiConfigureEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                             } else {
+                              NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                            }
                           } else {
-                            NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 1);
+                             if(configure) {
+                              NewMidiConfigureEvent(MIDI_EVENT_NOTE, chan, arg1, 1);
+                             } else {
+                              NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 1);
+                            }
                           }
                           break;
                        case CMD_NOTEOFF:
-                          NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                           if(configure) {
+                            NewMidiConfigureEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                           } else {
+                            NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                          }
                           break;
                        case CMD_CTRL:
-                          NewMidiEvent(MIDI_EVENT_CTRL, chan, arg1, arg2);
+                           if(configure) {
+                            NewMidiConfigureEvent(MIDI_EVENT_CTRL, chan, arg1, arg2);
+                           } else {
+                            NewMidiEvent(MIDI_EVENT_CTRL, chan, arg1, arg2);
+                          }
                           break;
                        case CMD_PITCH:
-                          NewMidiEvent(MIDI_EVENT_PITCH, chan, 0, arg1+128*arg2);
+                           if(configure) {
+                            NewMidiConfigureEvent(MIDI_EVENT_PITCH, chan, 0, arg1+128*arg2);
+                           } else {
+                            NewMidiEvent(MIDI_EVENT_PITCH, chan, 0, arg1+128*arg2);
+                          }
                           break;
                     }
                    state=STATE_SKIP;
@@ -149,42 +186,96 @@ static void *midi_thread(void *data) {
            }
         }
     }
+    return NULL;
+}
+
+void register_midi_device(int index) {
+    int i;
+    int ret=0;
+
+    if (index < 0 || index > MAX_MIDI_DEVICES) return;
+
+    g_print("%s: open MIDI device %d\n", __FUNCTION__, index);
+
+    if ((ret = snd_rawmidi_open(&midi_input[index], NULL, midi_port[index], SND_RAWMIDI_NONBLOCK)) < 0) {
+        fprintf(stderr,"cannot open port \"%s\": %s\n", midi_port[index], snd_strerror(ret));
+        return;
+    }
+    snd_rawmidi_read(midi_input[index], NULL, 0); /* trigger reading */
+
+
+    ret = pthread_create(&midi_thread_id[index], NULL, midi_thread, (void *) index);
+    if (ret < 0) {
+      g_print("%s: Failed to create MIDI read thread\n",__FUNCTION__);
+      if((ret = snd_rawmidi_close(midi_input[index])) < 0) {
+       g_print("%s: cannot close port: %s\n",__FUNCTION__, snd_strerror(ret));
+      }
+      return;
+    }
+    midi_devices[index].active=1;
+    return;
 }
 
-void register_midi_device(char *myname) {
+void close_midi_device(int index) {
+  int ret;
+
+  if (index < 0 || index > MAX_MIDI_DEVICES) return;
+  if (midi_devices[index].active == 0) return;
+
+  //
+  // Note that if this is called from get_midi_devices(),
+  // the port and device names do exist but may be wrong.
+  //
+  // Tell thread to stop
+  //
+  midi_devices[index].active=0;
+  //
+  // wait for thread to complete
+  //
+  ret=pthread_join(midi_thread_id[index], NULL);
+  if (ret  != 0)  {
+    g_print("%s: cannot join: %s\n", __FUNCTION__, strerror(ret));
+  }     
+  //
+  // Close MIDI device
+  if((ret = snd_rawmidi_close(midi_input[index])) < 0) {
+   g_print("%s: cannot close port: %s\n",__FUNCTION__, snd_strerror(ret));
+  }
+}
+
+void get_midi_devices() {
 
-    int mylen=strlen(myname);
     snd_ctl_t *ctl;
     snd_rawmidi_info_t *info;
     int card, device, subs, sub, ret;
     const char *devnam, *subnam;
     int found=0;
-    char name[64];
-    char *portname;
+    char portname[64];
 
+    n_midi_devices=0;
     card=-1;
     if ((ret = snd_card_next(&card)) < 0) {
         fprintf(stderr,"cannot determine card number: %s\n", snd_strerror(ret));
         return;
     }
     while (card >= 0) {
-       fprintf(stderr,"Found Sound Card=%d\n",card);
-       sprintf(name,"hw:%d", card);
-        if ((ret = snd_ctl_open(&ctl, name, 0)) < 0) {
+        //fprintf(stderr,"Found Sound Card=%d\n",card);
+        sprintf(portname,"hw:%d", card);
+        if ((ret = snd_ctl_open(&ctl, portname, 0)) < 0) {
                 fprintf(stderr,"cannot open control for card %d: %s\n", card, snd_strerror(ret));
                 return;
         }
-       device = -1;
-       // loop through devices of the card
-       for (;;) {
-           if ((ret = snd_ctl_rawmidi_next_device(ctl, &device)) < 0) {
+        device = -1;
+        // loop through devices of the card
+        for (;;) {
+            if ((ret = snd_ctl_rawmidi_next_device(ctl, &device)) < 0) {
                 fprintf(stderr,"cannot determine device number: %s\n", snd_strerror(ret));
                 break;
             }
-           if (device < 0) break;
-           fprintf(stderr,"Found Device=%d on Card=%d\n", device, card);
-           // found sub-device
-           snd_rawmidi_info_alloca(&info);
+            if (device < 0) break;
+            //fprintf(stderr,"Found Device=%d on Card=%d\n", device, card);
+            // found sub-device
+            snd_rawmidi_info_alloca(&info);
             snd_rawmidi_info_set_device(info, device);
             snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT);
             ret = snd_ctl_rawmidi_info(ctl, info);
@@ -192,10 +283,10 @@ void register_midi_device(char *myname) {
                 subs = snd_rawmidi_info_get_subdevices_count(info);
             } else {
                 subs = 0;
-           }
-           fprintf(stderr,"Number of MIDI input devices: %d\n", subs);
-           if (!subs) break;
-           // subs: number of sub-devices to device on card
+            }
+            //fprintf(stderr,"Number of MIDI input devices: %d\n", subs);
+            if (!subs) break;
+            // subs: number of sub-devices to device on card
             for (sub = 0; sub < subs; ++sub) {
                 snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT);
                 snd_rawmidi_info_set_subdevice(info, sub);
@@ -205,47 +296,70 @@ void register_midi_device(char *myname) {
                                    card, device, sub, snd_strerror(ret));
                     break;
                 }
-               devnam = snd_rawmidi_info_get_name(info);
-               subnam = snd_rawmidi_info_get_subdevice_name(info);
-               if (!strncmp(myname, devnam, mylen)) {
-                   found=1;
-                    // free() portname at the end of midi_thread
-                    portname=malloc(64);
-                   // If there is only one sub-device and it has no name, we  use
-                   // devnam for comparison and make a portname of form "hw:x,y",
-                   // else we use subnam for comparison and make a portname of form "hw:x,y,z".
-                    if (sub == 0 && subnam[0] == '\0') {
-                       sprintf(portname,"hw:%d,%d", card, device);
-                   } else {
-                       sprintf(portname,"hw:%d,%d,%d", card, device, sub);
-                       devnam=subnam;
-                   }
-                   fprintf(stderr,"MIDI device >%s< selected (PortName=>%s<)\n", devnam, portname);
-               } else {
-                    fprintf(stderr,"MIDI device >%s< not matching >%s<\n", devnam, myname);
-               }
                 if (found) break;
-           }
-            if (found) break;
-       }
-       snd_ctl_close(ctl);
-        if (found) break;
-       // next card
+                devnam = snd_rawmidi_info_get_name(info);
+                subnam = snd_rawmidi_info_get_subdevice_name(info);
+                // If there is only one sub-device and it has no name, we  use
+                // devnam for comparison and make a portname of form "hw:x,y",
+                // else we use subnam for comparison and make a portname of form "hw:x,y,z".
+                if (sub == 0 && subnam[0] == '\0') {
+                    sprintf(portname,"hw:%d,%d", card, device);
+                } else {
+                    sprintf(portname,"hw:%d,%d,%d", card, device, sub);
+                    devnam=subnam;
+                }
+                //
+                // If the name was already present at the same position, just keep
+                // it and do nothing.
+                // If the names do not match and the slot is occupied by a opened device,
+                // close it first
+                //
+                int match = 1;
+                if (midi_devices[n_midi_devices].name == NULL) {
+                  midi_devices[n_midi_devices].name=g_new(gchar,strlen(devnam)+1);
+                 strcpy(midi_devices[n_midi_devices].name, devnam);
+                  match = 0;
+                } else {
+                  if (strcmp(devnam, midi_devices[n_midi_devices].name)) {
+                    g_free(midi_devices[n_midi_devices].name);
+                    midi_devices[n_midi_devices].name=g_new(gchar,strlen(devnam)+1);
+                   strcpy(midi_devices[n_midi_devices].name, devnam);
+                    match = 0;
+                  }
+                }
+                if (midi_port[n_midi_devices] == NULL) {
+                  midi_port[n_midi_devices]=g_new(gchar,strlen(portname)+1);
+                 strcpy(midi_port[n_midi_devices], portname);
+                  match = 0;
+                } else {
+                  if (strcmp(midi_port[n_midi_devices], portname)) {
+                    g_free(midi_port[n_midi_devices]);
+                    midi_port[n_midi_devices]=g_new(gchar,strlen(portname)+1);
+                   strcpy(midi_port[n_midi_devices], portname);
+                    match = 0;
+                  }
+                }
+                //
+                // Close MIDI device if it was open, except if the device is
+                // the same as before. In this case, just let the thread
+                // proceed
+                //
+                if (match == 0 && midi_devices[n_midi_devices].active) {
+                  close_midi_device(n_midi_devices);
+                }
+                n_midi_devices++;
+            }
+        }
+        snd_ctl_close(ctl);
+        // next card
         if ((ret = snd_card_next(&card)) < 0) {
             fprintf(stderr,"cannot determine card number: %s\n", snd_strerror(ret));
             break;
         }
     }
-    if (!found) {
-       fprintf(stderr,"MIDI device %s NOT FOUND!\n", myname);
-        return;
-    }
-    // Found our MIDI input device. Spawn off a thread reading data
-    // (use the same variable midi_thread_id for all MIDI threads
-    // since it is not used again).
-    ret = pthread_create(&midi_thread_id, NULL, midi_thread, portname);
-    if (ret < 0) {
-       fprintf(stderr,"Failed to create MIDI read thread\n");
+
+    for(int i=0;i<n_midi_devices;i++) {
+        g_print("%s: %d: %s %s\n",__FUNCTION__,i,midi_devices[i].name,midi_port[i]);
     }
 }
 #endif
diff --git a/alsa_midi.h b/alsa_midi.h
new file mode 100644 (file)
index 0000000..2406b11
--- /dev/null
@@ -0,0 +1,11 @@
+typedef struct _midi_device {
+  char *name;
+  int  active;
+} MIDI_DEVICE;
+
+#define MAX_MIDI_DEVICES 10
+
+extern MIDI_DEVICE midi_devices[MAX_MIDI_DEVICES];
+extern int n_midi_devices;
+
+extern void get_midi_devices();
index 870187317e69f65888c09293c74bba81ba4ca478..11652bb84c053e7236e5b83fc8f97073bd2b793c 100644 (file)
@@ -18,7 +18,8 @@
 */
 
 #include <gtk/gtk.h>
-#include <gdk/gdk.h>
+#include <glib.h>
+#include <glib/gprintf.h>
 #include <math.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -30,6 +31,7 @@
 #include "main.h"
 #include "channel.h"
 #include "discovered.h"
+#include "actions.h"
 #include "gpio.h"
 #include "i2c.h"
 
 
 static GtkWidget *dialog;
 
-static GtkWidget *b_enable_vfo_encoder;
-static   GtkWidget *vfo_a_label;
-static   GtkWidget *vfo_a;
-static   GtkWidget *vfo_b_label;
-static   GtkWidget *vfo_b;
-static   GtkWidget *b_enable_vfo_pullup;
-static GtkWidget *b_enable_E2_encoder;
-static   GtkWidget *E2_a_label;
-static   GtkWidget *E2_a;
-static   GtkWidget *E2_b_label;
-static   GtkWidget *E2_b;
-static   GtkWidget *b_enable_E2_pullup;
-static GtkWidget *b_enable_E2_top_encoder;
-static   GtkWidget *E2_top_a_label;
-static   GtkWidget *E2_top_a;
-static   GtkWidget *E2_top_b_label;
-static   GtkWidget *E2_top_b;
-static GtkWidget *E2_sw_label;
-static   GtkWidget *E2_sw;
-static GtkWidget *b_enable_E3_encoder;
-static   GtkWidget *E3_a_label;
-static   GtkWidget *E3_a;
-static   GtkWidget *E3_b_label;
-static   GtkWidget *E3_b;
-static   GtkWidget *b_enable_E3_pullup;
-static GtkWidget *b_enable_E3_top_encoder;
-static   GtkWidget *E3_top_a_label;
-static   GtkWidget *E3_top_a;
-static   GtkWidget *E3_top_b_label;
-static   GtkWidget *E3_top_b;
-static GtkWidget *E3_sw_label;
-static   GtkWidget *E3_sw;
-static GtkWidget *b_enable_E4_encoder;
-static   GtkWidget *E4_a_label;
-static   GtkWidget *E4_a;
-static   GtkWidget *E4_b_label;
-static   GtkWidget *E4_b;
-static   GtkWidget *b_enable_E4_pullup;
-static GtkWidget *b_enable_E4_top_encoder;
-static   GtkWidget *E4_top_a_label;
-static   GtkWidget *E4_top_a;
-static   GtkWidget *E4_top_b_label;
-static   GtkWidget *E4_top_b;
-static GtkWidget *E4_sw_label;
-static   GtkWidget *E4_sw;
-static GtkWidget *b_enable_E5_encoder;
-static   GtkWidget *E5_a_label;
-static   GtkWidget *E5_a;
-static   GtkWidget *E5_b_label;
-static   GtkWidget *E5_b;
-static   GtkWidget *b_enable_E5_pullup;
-static GtkWidget *b_enable_E5_top_encoder;
-static   GtkWidget *E5_top_a_label;
-static   GtkWidget *E5_top_a;
-static   GtkWidget *E5_top_b_label;
-static   GtkWidget *E5_top_b;
-static GtkWidget *E5_sw_label;
-static   GtkWidget *E5_sw;
-
-static GtkWidget *b_enable_mox;
-static   GtkWidget *mox_label;
-static   GtkWidget *mox;
-static GtkWidget *b_enable_S1;
-static   GtkWidget *S1_label;
-static   GtkWidget *S1;
-static GtkWidget *b_enable_S2;
-static   GtkWidget *S2_label;
-static   GtkWidget *S2;
-static GtkWidget *b_enable_S3;
-static   GtkWidget *S3_label;
-static   GtkWidget *S3;
-static GtkWidget *b_enable_S4;
-static   GtkWidget *S4_label;
-static   GtkWidget *S4;
-static GtkWidget *b_enable_S5;
-static   GtkWidget *S5_label;
-static   GtkWidget *S5;
-static GtkWidget *b_enable_S6;
-static   GtkWidget *S6_label;
-static   GtkWidget *S6;
-static GtkWidget *b_enable_function;
-static   GtkWidget *function_label;
-static   GtkWidget *function;
-
-static GtkWidget *i2c_device_text;
-static GtkWidget *i2c_address;
 static GtkWidget *i2c_sw_text[16];
 
-
-#ifdef LOCALCW
-static GtkWidget *cwl_label;
-static GtkWidget *cwl;
-static GtkWidget *cwr_label;
-static GtkWidget *cwr;
-static GtkWidget *cws_label;
-static GtkWidget *cws;
-static GtkWidget *b_enable_cws;
-static GtkWidget *b_enable_cwlr;
-static GtkWidget *b_cw_active_low;
-#endif
-
-#ifdef PTT
-static GtkWidget *ptt_label;
-static GtkWidget *ptt;
-static GtkWidget *b_enable_ptt;
-static GtkWidget *b_ptt_active_low;
-#endif
-
-static gboolean save_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
-  if(dialog!=NULL) {
-    ENABLE_VFO_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_vfo_encoder))?1:0;
-    VFO_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(vfo_a));
-    VFO_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(vfo_b));
-    ENABLE_VFO_PULLUP=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_vfo_pullup))?1:0;
-
-    ENABLE_E2_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E2_encoder))?1:0;
-    E2_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E2_a));
-    E2_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E2_b));
-    ENABLE_E2_PULLUP=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E2_pullup))?1:0;
-    E2_FUNCTION=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E2_sw));
-
-    if(controller==CONTROLLER2_V2) {
-      E2_TOP_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E2_top_a));
-      E2_TOP_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E2_top_b));
-    }
-
-    ENABLE_E3_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E3_encoder))?1:0;
-    E3_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E3_a));
-    E3_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E3_b));
-    ENABLE_E3_PULLUP=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E3_pullup))?1:0;
-    E3_FUNCTION=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E3_sw));
-
-    if(controller==CONTROLLER2_V2) {
-      E3_TOP_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E3_top_a));
-      E3_TOP_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E3_top_b));
-    }
-
-    ENABLE_E4_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E4_encoder))?1:0;
-    E4_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E4_a));
-    E4_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E4_b));
-    ENABLE_E4_PULLUP=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E4_pullup))?1:0;
-    E4_FUNCTION=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E4_sw));
-
-    if(controller==CONTROLLER2_V2) {
-      E4_TOP_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E4_top_a));
-      E4_TOP_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E4_top_b));
-    }
-
-    if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
-      ENABLE_E5_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E5_encoder))?1:0;
-      E5_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E5_a));
-      E5_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E5_b));
-      ENABLE_E5_PULLUP=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E5_pullup))?1:0;
-      E5_FUNCTION=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E5_sw));
-
-      if(controller==CONTROLLER2_V2) {
-        E5_TOP_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E5_top_a));
-        E5_TOP_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E5_top_b));
-      }
-
-      const char *temp=gtk_entry_get_text(GTK_ENTRY(i2c_device_text));
-      i2c_device=g_new(char,strlen(temp)+1);
-      strcpy(i2c_device,temp);
-      i2c_address_1=(unsigned int)strtol(gtk_entry_get_text(GTK_ENTRY(i2c_address)),NULL,16);
-      for(int i=0;i<16;i++) {
-        i2c_sw[i]=(unsigned int)strtol(gtk_entry_get_text(GTK_ENTRY(i2c_sw_text[i])),NULL,16);
-      }
-    }
-
-  if(controller==CONTROLLER1) {
-    ENABLE_MOX_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_mox))?1:0;
-    MOX_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mox));
-
-    ENABLE_S1_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S1))?1:0;
-    S1_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S1));
-
-    ENABLE_S2_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S2))?1:0;
-    S2_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S2));
-
-    ENABLE_S3_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S3))?1:0;
-    S3_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S3));
-
-    ENABLE_S4_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S4))?1:0;
-    S4_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S4));
-
-    ENABLE_S5_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S5))?1:0;
-    S5_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S5));
-
-    ENABLE_S6_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S6))?1:0;
-    S6_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S6));
-
-    ENABLE_FUNCTION_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_function))?1:0;
-    FUNCTION_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(function));
-  }
-
-#ifdef LOCALCW
-    ENABLE_CW_BUTTONS=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_cwlr))?1:0;
-    CW_ACTIVE_LOW=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_cw_active_low))?1:0;
-    CWL_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(cwl));
-    CWR_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(cwr));
-    ENABLE_GPIO_SIDETONE=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_cws))?1:0;
-    SIDETONE_GPIO=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(cws));
-#endif
-
-#ifdef PTT
-    ENABLE_PTT_GPIO=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_ptt))?1:0;
-    PTT_ACTIVE_LOW=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_ptt_active_low))?1:0;
-    PTT_GPIO=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ptt));
-#endif
-
+static void response_event(GtkWidget *dialog,gint id,gpointer user_data) {
+  g_print("%s: id=%d\n",__FUNCTION__,id);
+  if(id==GTK_RESPONSE_ACCEPT) {
     gpio_save_state();
-    gtk_widget_destroy(dialog);
+    g_print("%s: ACCEPT\n",__FUNCTION__);
   }
-  return TRUE;
-}
-
-static gboolean cancel_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
-  if(dialog!=NULL) {
-    gtk_widget_destroy(dialog);
-  }
-  return TRUE;
+  gtk_widget_destroy(dialog);
+  dialog=NULL;
 }
 
 void configure_gpio(GtkWidget *parent) {
-  int row;
+  gint row=0;
+  gint col=0;
+  GtkWidget *widget;
+  int i;
 
   gpio_restore_state();
 
-  dialog=gtk_dialog_new_with_buttons("Configure GPIO (WiringPi pin numbers)",GTK_WINDOW(parent),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
-  GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
-  GtkWidget *grid=gtk_grid_new();
-  //gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
-  gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+  GtkWidget *dialog=gtk_dialog_new_with_buttons("piHPSDR - GPIO pins (Broadcom Numbers) ",GTK_WINDOW(parent),GTK_DIALOG_DESTROY_WITH_PARENT,("OK"),GTK_RESPONSE_ACCEPT,"Cancel",GTK_RESPONSE_REJECT,NULL);
 
-  GtkWidget *notebook=gtk_notebook_new();
+  g_signal_connect (dialog, "response", G_CALLBACK (response_event), NULL);
 
-  GtkWidget *grid0=gtk_grid_new();
-  gtk_grid_set_column_spacing (GTK_GRID(grid0),10);
+  GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
 
-  GtkWidget *save_b=gtk_button_new_with_label("Save");
-  g_signal_connect (save_b, "button_press_event", G_CALLBACK(save_cb), NULL);
-  gtk_grid_attach(GTK_GRID(grid0),save_b,0,0,1,1);
+  GtkWidget *notebook=gtk_notebook_new();
 
-  GtkWidget *cancel_b=gtk_button_new_with_label("Cancel");
-  g_signal_connect (cancel_b, "button_press_event", G_CALLBACK(cancel_cb), NULL);
-  gtk_grid_attach(GTK_GRID(grid0),cancel_b,1,0,1,1);
+  // Encoders
+  gint max_encoders=MAX_ENCODERS;
+  switch(controller) {
+    case NO_CONTROLLER:
+      max_encoders=0;
+      break;
+    case CONTROLLER1:
+      max_encoders=4;
+      break;
+    case CONTROLLER2_V1:
+      max_encoders=5;
+      break;
+    case CONTROLLER2_V2:
+      max_encoders=5;
+      break;
+  }
 
+  GtkWidget *grid=gtk_grid_new();
+  gtk_grid_set_column_homogeneous(GTK_GRID(grid),FALSE);
+  gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+  gtk_grid_set_column_spacing (GTK_GRID(grid),2);
+  gtk_grid_set_row_spacing (GTK_GRID(grid),2);
 
-  if(controller!=NO_CONTROLLER) {
 
-    // Encoders
-  
-    GtkWidget *grid1=gtk_grid_new();
-    gtk_grid_set_column_homogeneous(GTK_GRID(grid1),FALSE);
-    gtk_grid_set_row_homogeneous(GTK_GRID(grid1),TRUE);
-    gtk_grid_set_column_spacing (GTK_GRID(grid1),5);
-    row=0;
-  
-    b_enable_vfo_encoder=gtk_check_button_new_with_label("Enable VFO");
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_vfo_encoder), ENABLE_VFO_ENCODER);
-    gtk_widget_show(b_enable_vfo_encoder);
-    gtk_grid_attach(GTK_GRID(grid1),b_enable_vfo_encoder,0,row,1,1);
-  
-    vfo_a_label=gtk_label_new("GPIO A:");
-    gtk_widget_show(vfo_a_label);
-    gtk_grid_attach(GTK_GRID(grid1),vfo_a_label,1,row,1,1);
-  
-    vfo_a=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(vfo_a),VFO_ENCODER_A);
-    gtk_widget_show(vfo_a);
-    gtk_grid_attach(GTK_GRID(grid1),vfo_a,2,row,1,1);
-  
-    vfo_b_label=gtk_label_new("GPIO B:");
-    gtk_widget_show(vfo_b_label);
-    gtk_grid_attach(GTK_GRID(grid1),vfo_b_label,3,row,1,1);
-  
-    vfo_b=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(vfo_b),VFO_ENCODER_B);
-    gtk_widget_show(vfo_b);
-    gtk_grid_attach(GTK_GRID(grid1),vfo_b,4,row,1,1);
-
-    b_enable_vfo_pullup=gtk_check_button_new_with_label("Enable Pull-up");
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_vfo_pullup), ENABLE_VFO_PULLUP);
-    gtk_widget_show(b_enable_vfo_pullup);
-    gtk_grid_attach(GTK_GRID(grid1),b_enable_vfo_pullup,7,row,1,1);
-  
-  
-    row++;
-  
-    b_enable_E2_encoder=gtk_check_button_new_with_label("Enable E2");
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_E2_encoder), ENABLE_E2_ENCODER);
-    gtk_widget_show(b_enable_E2_encoder);
-    gtk_grid_attach(GTK_GRID(grid1),b_enable_E2_encoder,0,row,1,1);
-  
-    E2_a_label=gtk_label_new("GPIO A:");
-    gtk_widget_show(E2_a_label);
-    gtk_grid_attach(GTK_GRID(grid1),E2_a_label,1,row,1,1);
-  
-    E2_a=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(E2_a),E2_ENCODER_A);
-    gtk_widget_show(E2_a);
-    gtk_grid_attach(GTK_GRID(grid1),E2_a,2,row,1,1);
-  
-    E2_b_label=gtk_label_new("GPIO B:");
-    gtk_widget_show(E2_b_label);
-    gtk_grid_attach(GTK_GRID(grid1),E2_b_label,3,row,1,1);
-  
-    E2_b=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(E2_b),E2_ENCODER_B);
-    gtk_widget_show(E2_b);
-    gtk_grid_attach(GTK_GRID(grid1),E2_b,4,row,1,1);
-  
-    E2_sw_label=gtk_label_new("SW:");
-    gtk_widget_show(E2_sw_label);
-    gtk_grid_attach(GTK_GRID(grid1),E2_sw_label,5,row,1,1);
-  
-    E2_sw=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(E2_sw),E2_FUNCTION);
-    gtk_widget_show(E2_sw);
-    gtk_grid_attach(GTK_GRID(grid1),E2_sw,6,row,1,1);
-  
-    b_enable_E2_pullup=gtk_check_button_new_with_label("Enable Pull-up");
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_E2_pullup), ENABLE_E2_PULLUP);
-    gtk_widget_show(b_enable_E2_pullup);
-    gtk_grid_attach(GTK_GRID(grid1),b_enable_E2_pullup,7,row,1,1);
-  
-  
-    row++;
+/*
+  widget=gtk_label_new(NULL);
+  gtk_label_set_markup (GTK_LABEL(widget), "<span foreground=\"#ff0000\"><b>Note: Pin number now use Broadcom GPIO</b></span>");
+  gtk_grid_attach(GTK_GRID(grid),widget,col,row,6,1);
 
-    if(controller==CONTROLLER2_V2) {
-      E2_top_a_label=gtk_label_new("GPIO A:");
-      gtk_widget_show(E2_top_a_label);
-      gtk_grid_attach(GTK_GRID(grid1),E2_top_a_label,1,row,1,1);
+  row++;
+  col=0;
+*/
+  widget=gtk_label_new("");
+  gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+  col++;
+
+  widget=gtk_label_new(NULL);
+  gtk_label_set_markup (GTK_LABEL(widget), controller==CONTROLLER2_V2?"<b>Bottom Encoder</b>":"<b>Encoder</b>");
+  gtk_grid_attach(GTK_GRID(grid),widget,col,row,2,1);
+  col+=2;
+
+  if(controller==CONTROLLER2_V2) {
+    widget=gtk_label_new(NULL);
+    gtk_label_set_markup (GTK_LABEL(widget), "<b>Top Encoder</b>");
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,2,1);
+    col+=2;
+  }
 
-      E2_top_a=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(E2_top_a),E2_TOP_ENCODER_A);
-      gtk_widget_show(E2_top_a);
-      gtk_grid_attach(GTK_GRID(grid1),E2_top_a,2,row,1,1);
+  widget=gtk_label_new(NULL);
+  gtk_label_set_markup (GTK_LABEL(widget), "<b>Switch</b>");
+  gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
 
-      E2_top_b_label=gtk_label_new("GPIO B:");
-      gtk_widget_show(E2_top_b_label);
-      gtk_grid_attach(GTK_GRID(grid1),E2_top_b_label,3,row,1,1);
+  row++;
+  col=0;
+
+  widget=gtk_label_new(NULL);
+  gtk_label_set_markup (GTK_LABEL(widget), "<b>ID</b>");
+  gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+  col++;
+
+  widget=gtk_label_new(NULL);
+  gtk_label_set_markup (GTK_LABEL(widget), "<b>Gpio A</b>");
+  gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+  col++;
+
+  widget=gtk_label_new(NULL);
+  gtk_label_set_markup (GTK_LABEL(widget), "<b>Gpio B</b>");
+  gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+  col++;
+
+  if(controller==CONTROLLER2_V2) {
+    widget=gtk_label_new(NULL);
+    gtk_label_set_markup (GTK_LABEL(widget), "<b>Gpio A</b>");
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+
+    widget=gtk_label_new(NULL);
+    gtk_label_set_markup (GTK_LABEL(widget), "<b>Gpio B</b>");
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+  }
 
-      E2_top_b=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(E2_top_b),E2_TOP_ENCODER_B);
-      gtk_widget_show(E2_top_b);
-      gtk_grid_attach(GTK_GRID(grid1),E2_top_b,4,row,1,1);
+  widget=gtk_label_new(NULL);
+  gtk_label_set_markup (GTK_LABEL(widget), "<b>Gpio</b>");
+  gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+  col++;
 
-      row++;
+  row++;
+  col=0;
+
+  for(i=0;i<max_encoders;i++) {
+    widget=gtk_label_new(NULL);
+    gchar id[16];
+    g_sprintf(id,"<b>%d</b>",i);
+    gtk_label_set_markup (GTK_LABEL(widget), id);
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+
+    widget=gtk_spin_button_new_with_range (0.0,28.0,1.0);
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget),encoders[i].bottom_encoder_address_a);
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    
+    widget=gtk_spin_button_new_with_range (0.0,28.0,1.0);
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget),encoders[i].bottom_encoder_address_b);
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    
+    if(controller==CONTROLLER2_V2 && i<(max_encoders-1)) {
+      widget=gtk_spin_button_new_with_range (0.0,28.0,1.0);
+      gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget),encoders[i].top_encoder_address_a);
+      gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+      col++;
+   
+      widget=gtk_spin_button_new_with_range (0.0,28.0,1.0);
+      gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget),encoders[i].top_encoder_address_b);
+      gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+      col++;
     }
 
-    b_enable_E3_encoder=gtk_check_button_new_with_label("Enable E3");
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_E3_encoder), ENABLE_E3_ENCODER);
-    gtk_widget_show(b_enable_E3_encoder);
-    gtk_grid_attach(GTK_GRID(grid1),b_enable_E3_encoder,0,row,1,1);
-  
-    E3_a_label=gtk_label_new("GPIO A:");
-    gtk_widget_show(E3_a_label);
-    gtk_grid_attach(GTK_GRID(grid1),E3_a_label,1,row,1,1);
-  
-    E3_a=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(E3_a),E3_ENCODER_A);
-    gtk_widget_show(E3_a);
-    gtk_grid_attach(GTK_GRID(grid1),E3_a,2,row,1,1);
-  
-    E3_b_label=gtk_label_new("GPIO B:");
-    gtk_widget_show(E3_b_label);
-    gtk_grid_attach(GTK_GRID(grid1),E3_b_label,3,row,1,1);
-  
-    E3_b=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(E3_b),E3_ENCODER_B);
-    gtk_widget_show(E3_b);
-    gtk_grid_attach(GTK_GRID(grid1),E3_b,4,row,1,1);
-  
-    E3_sw_label=gtk_label_new("SW:");
-    gtk_widget_show(E3_sw_label);
-    gtk_grid_attach(GTK_GRID(grid1),E3_sw_label,5,row,1,1);
-  
-    E3_sw=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(E3_sw),E3_FUNCTION);
-    gtk_widget_show(E3_sw);
-    gtk_grid_attach(GTK_GRID(grid1),E3_sw,6,row,1,1);
-  
-    b_enable_E3_pullup=gtk_check_button_new_with_label("Enable Pull-up");
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_E3_pullup), ENABLE_E3_PULLUP);
-    gtk_widget_show(b_enable_E3_pullup);
-    gtk_grid_attach(GTK_GRID(grid1),b_enable_E3_pullup,7,row,1,1);
-  
-    row++;
-
-    if(controller==CONTROLLER2_V2) {
-      E3_top_a_label=gtk_label_new("GPIO A:");
-      gtk_widget_show(E3_top_a_label);
-      gtk_grid_attach(GTK_GRID(grid1),E3_top_a_label,1,row,1,1);
-
-      E3_top_a=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(E3_top_a),E3_TOP_ENCODER_A);
-      gtk_widget_show(E3_top_a);
-      gtk_grid_attach(GTK_GRID(grid1),E3_top_a,2,row,1,1);
-
-      E3_top_b_label=gtk_label_new("GPIO B:");
-      gtk_widget_show(E3_top_b_label);
-      gtk_grid_attach(GTK_GRID(grid1),E3_top_b_label,3,row,1,1);
-
-      E3_top_b=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(E3_top_b),E3_TOP_ENCODER_B);
-      gtk_widget_show(E3_top_b);
-      gtk_grid_attach(GTK_GRID(grid1),E3_top_b,4,row,1,1);
-
-      row++;
+    if(i<(max_encoders-1)) {
+      widget=gtk_spin_button_new_with_range (0.0,28.0,1.0);
+      gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget),encoders[i].switch_address);
+      gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+      col++;
     }
 
-    b_enable_E4_encoder=gtk_check_button_new_with_label("Enable E4");
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_E4_encoder), ENABLE_E4_ENCODER);
-    gtk_widget_show(b_enable_E4_encoder);
-    gtk_grid_attach(GTK_GRID(grid1),b_enable_E4_encoder,0,row,1,1);
-  
-    E4_a_label=gtk_label_new("GPIO A:");
-    gtk_widget_show(E4_a_label);
-    gtk_grid_attach(GTK_GRID(grid1),E4_a_label,1,row,1,1);
-  
-    E4_a=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(E4_a),E4_ENCODER_A);
-    gtk_widget_show(E4_a);
-    gtk_grid_attach(GTK_GRID(grid1),E4_a,2,row,1,1);
-  
-    E4_b_label=gtk_label_new("GPIO B:");
-    gtk_widget_show(E4_b_label);
-    gtk_grid_attach(GTK_GRID(grid1),E4_b_label,3,row,1,1);
-  
-    E4_b=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(E4_b),E4_ENCODER_B);
-    gtk_widget_show(E4_b);
-    gtk_grid_attach(GTK_GRID(grid1),E4_b,4,row,1,1);
-  
-    E4_sw_label=gtk_label_new("SW:");
-    gtk_widget_show(E4_sw_label);
-    gtk_grid_attach(GTK_GRID(grid1),E4_sw_label,5,row,1,1);
-  
-    E4_sw=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(E4_sw),E4_FUNCTION);
-    gtk_widget_show(E4_sw);
-    gtk_grid_attach(GTK_GRID(grid1),E4_sw,6,row,1,1);
-  
-    b_enable_E4_pullup=gtk_check_button_new_with_label("Enable Pull-up");
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_E4_pullup), ENABLE_E4_PULLUP);
-    gtk_widget_show(b_enable_E4_pullup);
-    gtk_grid_attach(GTK_GRID(grid1),b_enable_E4_pullup,7,row,1,1);
-  
     row++;
+    col=0;
+  }
+  gtk_notebook_append_page(GTK_NOTEBOOK(notebook),grid,gtk_label_new("Encoders"));
 
-    if(controller==CONTROLLER2_V2)
-    {
-      E4_top_a_label=gtk_label_new("GPIO A:");
-      gtk_widget_show(E4_top_a_label);
-      gtk_grid_attach(GTK_GRID(grid1),E4_top_a_label,1,row,1,1);
-
-      E4_top_a=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(E4_top_a),E4_TOP_ENCODER_A);
-      gtk_widget_show(E4_top_a);
-      gtk_grid_attach(GTK_GRID(grid1),E4_top_a,2,row,1,1);
-
-      E4_top_b_label=gtk_label_new("GPIO B:");
-      gtk_widget_show(E4_top_b_label);
-      gtk_grid_attach(GTK_GRID(grid1),E4_top_b_label,3,row,1,1);
-
-      E4_top_b=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(E4_top_b),E4_TOP_ENCODER_B);
-      gtk_widget_show(E4_top_b);
-      gtk_grid_attach(GTK_GRID(grid1),E4_top_b,4,row,1,1);
-
-      row++;
-    }
 
-    if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
-      b_enable_E5_encoder=gtk_check_button_new_with_label("Enable E5");
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_E5_encoder), ENABLE_E5_ENCODER);
-      gtk_widget_show(b_enable_E5_encoder);
-      gtk_grid_attach(GTK_GRID(grid1),b_enable_E5_encoder,0,row,1,1);
-
-      E5_a_label=gtk_label_new("GPIO A:");
-      gtk_widget_show(E5_a_label);
-      gtk_grid_attach(GTK_GRID(grid1),E5_a_label,1,row,1,1);
-
-      E5_a=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(E5_a),E5_ENCODER_A);
-      gtk_widget_show(E5_a);
-      gtk_grid_attach(GTK_GRID(grid1),E5_a,2,row,1,1);
-
-      E5_b_label=gtk_label_new("GPIO B:");
-      gtk_widget_show(E5_b_label);
-      gtk_grid_attach(GTK_GRID(grid1),E5_b_label,3,row,1,1);
-
-      E5_b=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(E5_b),E5_ENCODER_B);
-      gtk_widget_show(E5_b);
-      gtk_grid_attach(GTK_GRID(grid1),E5_b,4,row,1,1);
-
-      E5_sw_label=gtk_label_new("SW:");
-      gtk_widget_show(E5_sw_label);
-      gtk_grid_attach(GTK_GRID(grid1),E5_sw_label,5,row,1,1);
-
-      E5_sw=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(E5_sw),E5_FUNCTION);
-      gtk_widget_show(E5_sw);
-      gtk_grid_attach(GTK_GRID(grid1),E5_sw,6,row,1,1);
-
-      b_enable_E5_pullup=gtk_check_button_new_with_label("Enable Pull-up");
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_E5_pullup), ENABLE_E5_PULLUP);
-      gtk_widget_show(b_enable_E5_pullup);
-      gtk_grid_attach(GTK_GRID(grid1),b_enable_E5_pullup,7,row,1,1);
-
-      if(controller==CONTROLLER2_V2) {
-        row++;
-
-        E5_top_a_label=gtk_label_new("GPIO A:");
-        gtk_widget_show(E5_top_a_label);
-        gtk_grid_attach(GTK_GRID(grid1),E5_top_a_label,1,row,1,1);
-
-        E5_top_a=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-        gtk_spin_button_set_value (GTK_SPIN_BUTTON(E5_top_a),E5_TOP_ENCODER_A);
-        gtk_widget_show(E5_top_a);
-        gtk_grid_attach(GTK_GRID(grid1),E5_top_a,2,row,1,1);
-
-        E5_top_b_label=gtk_label_new("GPIO B:");
-        gtk_widget_show(E5_top_b_label);
-        gtk_grid_attach(GTK_GRID(grid1),E5_top_b_label,3,row,1,1);
-
-        E5_top_b=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-        gtk_spin_button_set_value (GTK_SPIN_BUTTON(E5_top_b),E5_TOP_ENCODER_B);
-        gtk_widget_show(E5_top_b);
-        gtk_grid_attach(GTK_GRID(grid1),E5_top_b,4,row,1,1);
-      }
+  // switches
+  if(controller==CONTROLLER1) {
+    gint max_switches=MAX_SWITCHES;
+    switch(controller) {
+      case NO_CONTROLLER:
+        max_switches=0;
+        break;
+      case CONTROLLER1:
+        max_switches=8;
+        break;
+      case CONTROLLER2_V1:
+        max_switches=0;
+        break;
+      case CONTROLLER2_V2:
+        max_switches=0;
+        break;
     }
   
-    gtk_notebook_append_page(GTK_NOTEBOOK(notebook),grid1,gtk_label_new("Encoders"));
-  
-    // Switches
-  
-    GtkWidget *grid2=gtk_grid_new();
-    gtk_grid_set_column_spacing (GTK_GRID(grid2),10);
-    row=0;
-
-
-    if(controller==CONTROLLER1) {
-      b_enable_mox=gtk_check_button_new_with_label("Enable MOX/TUN");
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_mox), ENABLE_MOX_BUTTON);
-      gtk_widget_show(b_enable_mox);
-      gtk_grid_attach(GTK_GRID(grid2),b_enable_mox,0,row,1,1);
-  
-      mox_label=gtk_label_new("GPIO:");
-      gtk_widget_show(mox_label);
-      gtk_grid_attach(GTK_GRID(grid2),mox_label,1,row,1,1);
-  
-      mox=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(mox),MOX_BUTTON);
-      gtk_widget_show(mox);
-      gtk_grid_attach(GTK_GRID(grid2),mox,2,row,1,1);
-
-      row++;
-  
-      b_enable_S1=gtk_check_button_new_with_label("Enable S1");
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_S1), ENABLE_S1_BUTTON);
-      gtk_widget_show(b_enable_S1);
-      gtk_grid_attach(GTK_GRID(grid2),b_enable_S1,0,row,1,1);
-  
-      S1_label=gtk_label_new("GPIO:");
-      gtk_widget_show(S1_label);
-      gtk_grid_attach(GTK_GRID(grid2),S1_label,1,row,1,1);
-  
-      S1=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(S1),S1_BUTTON);
-      gtk_widget_show(S1);
-      gtk_grid_attach(GTK_GRID(grid2),S1,2,row,1,1);
-
-      row++;
+    grid=gtk_grid_new();
+    gtk_grid_set_column_homogeneous(GTK_GRID(grid),FALSE);
+    gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+    gtk_grid_set_column_spacing (GTK_GRID(grid),2);
+    gtk_grid_set_row_spacing (GTK_GRID(grid),2);
 
-      b_enable_S2=gtk_check_button_new_with_label("Enable S2");
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_S2), ENABLE_S2_BUTTON);
-      gtk_widget_show(b_enable_S2);
-      gtk_grid_attach(GTK_GRID(grid),b_enable_S2,0,row,1,1);
-    
-      S2_label=gtk_label_new("GPIO:");
-      gtk_widget_show(S2_label);
-      gtk_grid_attach(GTK_GRID(grid),S2_label,1,row,1,1);
-  
-      S2=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(S2),S2_BUTTON);
-      gtk_widget_show(S2);
-      gtk_grid_attach(GTK_GRID(grid),S2,2,row,1,1);
+    row=0;
+    col=0;
   
-      row++;
+/*
+    widget=gtk_label_new(NULL);
+    gtk_label_set_markup (GTK_LABEL(widget), "<span foreground=\"#ff0000\"><b>Note: Pin number now use Broadcom GPIO</b></span>");
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,6,1);
   
-      b_enable_S3=gtk_check_button_new_with_label("Enable S3");
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_S3), ENABLE_S3_BUTTON);
-      gtk_widget_show(b_enable_S3);
-      gtk_grid_attach(GTK_GRID(grid),b_enable_S3,0,row,1,1);
-    
-      S3_label=gtk_label_new("GPIO:");
-      gtk_widget_show(S3_label);
-      gtk_grid_attach(GTK_GRID(grid),S3_label,1,row,1,1);
-    
-      S3=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(S3),S3_BUTTON);
-      gtk_widget_show(S3);
-      gtk_grid_attach(GTK_GRID(grid),S3,2,row,1,1);
-
-      row++;
-
-      b_enable_S4=gtk_check_button_new_with_label("Enable S4");
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_S4), ENABLE_S4_BUTTON);
-      gtk_widget_show(b_enable_S4);
-      gtk_grid_attach(GTK_GRID(grid2),b_enable_S4,0,row,1,1);
-    
-      S4_label=gtk_label_new("GPIO:");
-      gtk_widget_show(S4_label);
-      gtk_grid_attach(GTK_GRID(grid2),S4_label,1,row,1,1);
-    
-      S4=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(S4),S4_BUTTON);
-      gtk_widget_show(S4);
-      gtk_grid_attach(GTK_GRID(grid2),S4,2,row,1,1);
-    
-      row++;
+    row++;
+    col=0;
+*/
+    for(i=0;i<max_switches/8;i++) {
+      widget=gtk_label_new(NULL);
+      gtk_label_set_markup (GTK_LABEL(widget), "<b>ID</b>");
+      gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+      col++;
     
-      b_enable_S5=gtk_check_button_new_with_label("Enable S5");
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_S5), ENABLE_S5_BUTTON);
-      gtk_widget_show(b_enable_S5);
-      gtk_grid_attach(GTK_GRID(grid2),b_enable_S5,0,row,1,1);
+      widget=gtk_label_new(NULL);
+      gtk_label_set_markup (GTK_LABEL(widget), "<b>Gpio</b>");
+      gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+      col++;
+    }
   
-      S5_label=gtk_label_new("GPIO:");
-      gtk_widget_show(S5_label);
-      gtk_grid_attach(GTK_GRID(grid2),S5_label,1,row,1,1);
-    
-      S5=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(S5),S5_BUTTON);
-      gtk_widget_show(S5);
-      gtk_grid_attach(GTK_GRID(grid2),S5,2,row,1,1);
-    
-      row++;
-    
-      b_enable_S6=gtk_check_button_new_with_label("Enable S6");
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_S6), ENABLE_S6_BUTTON);
-      gtk_widget_show(b_enable_S6);
-      gtk_grid_attach(GTK_GRID(grid2),b_enable_S6,0,row,1,1);
-    
-      S6_label=gtk_label_new("GPIO:");
-      gtk_widget_show(S6_label);
-      gtk_grid_attach(GTK_GRID(grid2),S6_label,1,row,1,1);
-    
-      S6=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(S6),S6_BUTTON);
-      gtk_widget_show(S6);
-      gtk_grid_attach(GTK_GRID(grid2),S6,2,row,1,1);
-    
-      row++;
-    
-      b_enable_function=gtk_check_button_new_with_label("Enable Function");
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_function), ENABLE_FUNCTION_BUTTON);
-      gtk_widget_show(b_enable_function);
-      gtk_grid_attach(GTK_GRID(grid2),b_enable_function,0,row,1,1);
-    
-      function_label=gtk_label_new("GPIO:");
-      gtk_widget_show(function_label);
-      gtk_grid_attach(GTK_GRID(grid2),function_label,1,row,1,1);
+    row++;
+    col=0;
+  
+    for(i=0;i<max_switches;i++) {
+      widget=gtk_label_new(NULL);
+      gchar id[16];
+      g_sprintf(id,"<b>%d</b>",i);
+      gtk_label_set_markup (GTK_LABEL(widget), id);
+      gtk_grid_attach(GTK_GRID(grid),widget,(i/8)*2,(row+(i%8)),1,1);
+  
+      widget=gtk_spin_button_new_with_range (0.0,28.0,1.0);
+      gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget),switches[i].switch_address);
+      gtk_grid_attach(GTK_GRID(grid),widget,((i/8)*2)+1,(row+(i%8)),1,1);
+    }
     
-      function=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-      gtk_spin_button_set_value (GTK_SPIN_BUTTON(function),FUNCTION_BUTTON);
-      gtk_widget_show(function);
-      gtk_grid_attach(GTK_GRID(grid2),function,2,row,1,1);
-
-      gtk_notebook_append_page(GTK_NOTEBOOK(notebook),grid2,gtk_label_new("Switches"));
-    } else {
-      char text[16];
-      GtkWidget *grid2=gtk_grid_new();
-      gtk_grid_set_column_spacing (GTK_GRID(grid2),10);
-      row=0;
-  
-      GtkWidget *label=gtk_label_new("I2C Device:");
-      gtk_widget_show(label);
-      gtk_grid_attach(GTK_GRID(grid2),label,0,row,1,1);
-  
-      i2c_device_text=gtk_entry_new();
-      gtk_widget_show(i2c_device_text);
-      gtk_entry_set_text (GTK_ENTRY(i2c_device_text),i2c_device);
-      gtk_grid_attach(GTK_GRID(grid2),i2c_device_text,1,row,1,1);
-     
-      label=gtk_label_new("I2C Address:");
-      gtk_widget_show(label);
-      gtk_grid_attach(GTK_GRID(grid2),label,2,row,1,1);
-  
-      i2c_address=gtk_entry_new();
-      sprintf(text,"0x%02X",i2c_address_1);
-      gtk_entry_set_text (GTK_ENTRY(i2c_address),text);
-      gtk_widget_show(i2c_address);
-      gtk_grid_attach(GTK_GRID(grid2),i2c_address,3,row,1,1);
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook),grid,gtk_label_new("switches"));
+  }
 
-      row++;
+  if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
+    grid=gtk_grid_new();
+    gtk_grid_set_column_homogeneous(GTK_GRID(grid),FALSE);
+    gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+    gtk_grid_set_column_spacing (GTK_GRID(grid),2);
+    gtk_grid_set_row_spacing (GTK_GRID(grid),2);
 
-      for(int i=0;i<8;i++) {
-        sprintf(text,"SW_%d",i+2);
-        label=gtk_label_new(text);
-        gtk_widget_show(label);
-        gtk_grid_attach(GTK_GRID(grid2),label,0,row,1,1);
+    row=0;
+    col=0;
+    char text[16];
+    grid=gtk_grid_new();
+    gtk_grid_set_column_spacing (GTK_GRID(grid),10);
+
+    widget=gtk_label_new(NULL);
+    gtk_label_set_markup(GTK_LABEL(widget),"<b>I2C Device</b>");
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+
+    widget=gtk_entry_new();
+    gtk_entry_set_text(GTK_ENTRY(widget),i2c_device);
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+
+    widget=gtk_label_new(NULL);
+    gtk_label_set_markup(GTK_LABEL(widget),"<b>I2C Address</b>");
+    gtk_widget_show(widget);
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+
+    widget=gtk_entry_new();
+    sprintf(text,"0x%02X",i2c_address_1);
+    gtk_entry_set_text(GTK_ENTRY(widget),text);
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
 
-        i2c_sw_text[i]=gtk_entry_new();
-        sprintf(text,"0x%04X",i2c_sw[i]);
-        gtk_entry_set_text (GTK_ENTRY(i2c_sw_text[i]),text);
-        gtk_widget_show(i2c_sw_text[i]);
-        gtk_grid_attach(GTK_GRID(grid2),i2c_sw_text[i],1,row,1,1);
+    row++;
+    col=0;
 
-        sprintf(text,"SW_%d",i+10);
-        label=gtk_label_new(text);
-        gtk_widget_show(label);
-        gtk_grid_attach(GTK_GRID(grid2),label,2,row,1,1);
+    for(int i=0;i<8;i++) {
+      widget=gtk_label_new(NULL);
+      sprintf(text,"<b>SW_%d</b>",i+2);
+      gtk_label_set_markup(GTK_LABEL(widget),text);
+      gtk_grid_attach(GTK_GRID(grid),widget,0,row,1,1);
 
-        i2c_sw_text[i+8]=gtk_entry_new();
-        sprintf(text,"0x%04X",i2c_sw[i+8]);
-        gtk_entry_set_text (GTK_ENTRY(i2c_sw_text[i+8]),text);
-        gtk_widget_show(i2c_sw_text[i+8]);
-        gtk_grid_attach(GTK_GRID(grid2),i2c_sw_text[i+8],3,row,1,1);
+      i2c_sw_text[i]=gtk_entry_new();
+      sprintf(text,"0x%04X",i2c_sw[i]);
+      gtk_entry_set_text (GTK_ENTRY(i2c_sw_text[i]),text);
+      gtk_grid_attach(GTK_GRID(grid),i2c_sw_text[i],1,row,1,1);
 
-        row++;
+      widget=gtk_label_new(NULL);
+      sprintf(text,"<b>SW_%d</b>",i+10);
+      gtk_label_set_markup(GTK_LABEL(widget),text);
+      gtk_grid_attach(GTK_GRID(grid),widget,2,row,1,1);
 
-      }
+      i2c_sw_text[i+8]=gtk_entry_new();
+      sprintf(text,"0x%04X",i2c_sw[i+8]);
+      gtk_entry_set_text (GTK_ENTRY(i2c_sw_text[i+8]),text);
+      gtk_grid_attach(GTK_GRID(grid),i2c_sw_text[i+8],3,row,1,1);
 
-      gtk_notebook_append_page(GTK_NOTEBOOK(notebook),grid2,gtk_label_new("I2C"));
+      row++;
     }
-
+    
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook),grid,gtk_label_new("i2c"));
   }
 
-#ifdef LOCALCW
-  // CW
-
-  GtkWidget *grid3=gtk_grid_new();
-  gtk_grid_set_column_spacing (GTK_GRID(grid3),10);
-  row=0;
-
-  cwl_label=gtk_label_new("CWL GPIO:");
-  gtk_widget_show(cwl_label);
-  gtk_grid_attach(GTK_GRID(grid3),cwl_label,0,row,1,1);
-
-  cwl=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-  gtk_spin_button_set_value (GTK_SPIN_BUTTON(cwl),CWL_BUTTON);
-  gtk_widget_show(cwl);
-  gtk_grid_attach(GTK_GRID(grid3),cwl,1,row,1,1);
-
-  b_enable_cwlr=gtk_check_button_new_with_label("CWLR Enable");
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_cwlr), ENABLE_CW_BUTTONS);
-  gtk_widget_show(b_enable_cwlr);
-  gtk_grid_attach(GTK_GRID(grid3),b_enable_cwlr,2,row,1,1);
-
-  row++;
-
-  cwr_label=gtk_label_new("CWR GPIO:");
-  gtk_widget_show(cwr_label);
-  gtk_grid_attach(GTK_GRID(grid3),cwr_label,0,row,1,1);
-
-  cwr=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-  gtk_spin_button_set_value (GTK_SPIN_BUTTON(cwr),CWR_BUTTON);
-  gtk_widget_show(cwr);
-  gtk_grid_attach(GTK_GRID(grid3),cwr,1,row,1,1);
-
-  b_cw_active_low=gtk_check_button_new_with_label("CWLR active-low");
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_cw_active_low), CW_ACTIVE_LOW);
-  gtk_widget_show(b_cw_active_low);
-  gtk_grid_attach(GTK_GRID(grid3),b_cw_active_low,2,row,1,1);
-
-  row++;
-
-  cws_label=gtk_label_new("  SideTone GPIO:");
-  gtk_widget_show(cws_label);
-  gtk_grid_attach(GTK_GRID(grid3),cws_label,0,row,1,1);
-
-  cws=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-  gtk_spin_button_set_value (GTK_SPIN_BUTTON(cws),SIDETONE_GPIO);
-  gtk_widget_show(cws);
-  gtk_grid_attach(GTK_GRID(grid3),cws,1,row,1,1);
-
-  b_enable_cws=gtk_check_button_new_with_label("Enable");
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_cws), ENABLE_GPIO_SIDETONE);
-  gtk_widget_show(b_enable_cws);
-  gtk_grid_attach(GTK_GRID(grid3),b_enable_cws,2,row,1,1);
-
-  gtk_notebook_append_page(GTK_NOTEBOOK(notebook),grid3,gtk_label_new("CW"));
-#endif
-
-#ifdef PTT
-  GtkWidget *grid4=gtk_grid_new();
-  gtk_grid_set_column_spacing (GTK_GRID(grid4),10);
-  row=0;
-
-  b_enable_ptt=gtk_check_button_new_with_label("PTT Enable");
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_ptt), ENABLE_PTT_GPIO);
-  gtk_widget_show(b_enable_ptt);
-  gtk_grid_attach(GTK_GRID(grid4),b_enable_ptt,0,row,1,1);
-
-  row++;
-
-  ptt_label=gtk_label_new("PTT GPIO:");
-  gtk_widget_show(ptt_label);
-  gtk_grid_attach(GTK_GRID(grid4),ptt_label,0,row,1,1);
-
-  ptt=gtk_spin_button_new_with_range (0.0,100.0,1.0);
-  gtk_spin_button_set_value (GTK_SPIN_BUTTON(ptt),CWR_BUTTON);
-  gtk_widget_show(ptt);
-  gtk_grid_attach(GTK_GRID(grid4),ptt,1,row,1,1);
-
-  row++;
-
-  b_ptt_active_low=gtk_check_button_new_with_label("PTT active-low");
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_ptt_active_low), PTT_ACTIVE_LOW);
-  gtk_widget_show(b_ptt_active_low);
-  gtk_grid_attach(GTK_GRID(grid4),b_ptt_active_low,0,row,1,1);
-
-  gtk_notebook_append_page(GTK_NOTEBOOK(notebook),grid4,gtk_label_new("PTT"));
-#endif
-
-  gtk_grid_attach(GTK_GRID(grid0),notebook,0,1,6,1);
-  gtk_container_add(GTK_CONTAINER(content),grid0);
+  gtk_container_add(GTK_CONTAINER(content),notebook);
 
   gtk_widget_show_all(dialog);
   gtk_dialog_run(GTK_DIALOG(dialog));
-
 }
 #endif
 
diff --git a/css.c b/css.c
new file mode 100644 (file)
index 0000000..c8b9ae3
--- /dev/null
+++ b/css.c
@@ -0,0 +1,26 @@
+#include <gtk/gtk.h>
+
+char *css=
+"  #small {\n"
+"    padding: 0;\n"
+"    font-family: Sans;\n"
+"    font-size: 12px;\n"
+"    }\n"
+;
+
+void load_css() {
+  GtkCssProvider *provider;
+  GdkDisplay *display;
+  GdkScreen *screen;
+
+  g_print("%s\n",__FUNCTION__);
+  provider = gtk_css_provider_new ();
+  display = gdk_display_get_default ();
+  screen = gdk_display_get_default_screen (display);
+  gtk_style_context_add_provider_for_screen (screen,
+                                             GTK_STYLE_PROVIDER(provider),
+                                             GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+  gtk_css_provider_load_from_data(provider, css, -1, NULL);
+  g_object_unref (provider);
+}
diff --git a/dac.h b/dac.h
index 5bd9ad4df4c53865aaa6b53f928683f2cca25365..a08135f6bec3ad18ed00723054668ba2b659492b 100644 (file)
--- a/dac.h
+++ b/dac.h
@@ -22,7 +22,7 @@
 
 typedef struct _dac {
   gint antenna;
-  gint *tx_gain;
+  gdouble gain;
 } DAC;
 
 #endif
index dc71bfcb0dbc30a41038184f861691460c547134..2764b83cf89019b66eeca5fd35110ad8b23ff2c2 100644 (file)
@@ -117,6 +117,7 @@ struct _DISCOVERED {
         char hardware_key[64];
         char driver_key[64];
         int rtlsdr_count;
+        int sdrplay_count;
         int sample_rate;
         size_t rx_channels;
         size_t rx_gains;
@@ -132,6 +133,10 @@ struct _DISCOVERED {
         SoapySDRRange *tx_range;
         size_t tx_antennas;
         char **tx_antenna;
+        size_t sensors;
+        char **sensor;
+        gboolean has_temp;
+        char address[64];
       } soapy;
 #endif
     } info;
index 8cd3f441f3a0f707a58a51165a2874274689b5e7..5966027e4930765786d0d3b765e6fa73635253f0 100644 (file)
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#ifdef MIDI
-#include <sys/stat.h>
-#include <fcntl.h>
-#endif
 
 #include "discovered.h"
 #include "old_discovery.h"
@@ -120,103 +116,6 @@ static gboolean start_cb (GtkWidget *widget, GdkEventButton *event, gpointer dat
   return TRUE;
 }
 
-#ifdef MIDI
-//
-// This is a file open dialog. If we choose a readable file here, it is just copied
-// to file "midi.props" in the local directory
-//
-static gboolean midi_cb(GtkWidget *widget, GdkEventButton *event, gpointer data) {
-    GtkWidget *opfile,*message;
-    GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
-    gint res;
-    int fdin, fdout;
-    size_t len,bytes_read,bytes_written;
-
-    opfile = gtk_file_chooser_dialog_new ("Import MIDI description",
-                                      GTK_WINDOW(top_window),
-                                      action,
-                                      "Cancel",
-                                      GTK_RESPONSE_CANCEL,
-                                      "Open",
-                                      GTK_RESPONSE_ACCEPT,
-                                      NULL);
-
-    res = gtk_dialog_run (GTK_DIALOG (opfile));
-    if (res == GTK_RESPONSE_ACCEPT) {
-      char *filename, *cp;
-      struct stat statbuf;
-      GtkFileChooser *chooser = GTK_FILE_CHOOSER (opfile);
-      char *contents = NULL;
-      filename = gtk_file_chooser_get_filename (chooser);
-      fdin =open(filename, O_RDONLY);
-      bytes_read = bytes_written = 0;
-      if (fdin >= 0) {
-        fstat(fdin, &statbuf);
-        len=statbuf.st_size;
-        //
-        // Now first read the whole contents of the file, and then write it out.
-        // This is for new-bees trying to import the midi.props in the working dir
-        //
-        contents=g_new(char, len);
-        bytes_read = bytes_written = 0;
-        if (contents) {
-          bytes_read=read(fdin, contents, len);
-        }
-        close(fdin);
-      }
-      fdout=0;
-      if (contents && bytes_read == len) {
-       // should this file exist as a link or symlink, or should it
-       // be read-only, remove it first
-       unlink("midi.props");
-        fdout=open("midi.props", O_WRONLY | O_CREAT, 0644);
-        if (fdout >= 0) {
-          bytes_written=write(fdout, contents, len);
-          close(fdout);
-          g_free(contents);
-        }
-      }
-      if (fdin < 0 || bytes_read < len) {
-        message = gtk_message_dialog_new (GTK_WINDOW(top_window),
-               GTK_DIALOG_DESTROY_WITH_PARENT,
-               GTK_MESSAGE_ERROR,
-               GTK_BUTTONS_CLOSE,
-               "Cannot read input file!\n");
-        gtk_dialog_run (GTK_DIALOG (message));
-        gtk_widget_destroy(message);
-      } else if (fdout < 0 || bytes_written < len) {
-        message = gtk_message_dialog_new (GTK_WINDOW(top_window),
-               GTK_DIALOG_DESTROY_WITH_PARENT,
-               GTK_MESSAGE_ERROR,
-               GTK_BUTTONS_CLOSE,
-               "Cannot write MIDI settings!\n");
-        gtk_dialog_run (GTK_DIALOG (message));
-        gtk_widget_destroy(message);
-      } else {
-       // only show basename in the message
-       cp = filename + strlen(filename);
-        while (cp >= filename) {
-         if (*cp == '/') {
-           cp++;
-           break;
-         }
-         cp--;
-       }
-        message = gtk_message_dialog_new (GTK_WINDOW(top_window),
-               GTK_DIALOG_DESTROY_WITH_PARENT,
-               GTK_MESSAGE_ERROR,
-               GTK_BUTTONS_CLOSE,
-               "MIDI import: %ld Bytes read from file %s\n",len,cp);
-        gtk_dialog_run (GTK_DIALOG (message));
-        gtk_widget_destroy(message);
-      }
-      g_free(filename);
-    }
-    gtk_widget_destroy (opfile);
-    return TRUE;
-}
-#endif
-
 static gboolean protocols_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
   configure_protocols(discovery_dialog);
   return TRUE;
@@ -599,12 +498,6 @@ fprintf(stderr,"%p Protocol=%d name=%s\n",d,d->protocol,d->name);
     g_signal_connect (protocols_b, "button-press-event", G_CALLBACK(protocols_cb), NULL);
     gtk_grid_attach(GTK_GRID(grid),protocols_b,2,row,1,1);
 
-#ifdef MIDI
-    GtkWidget *midi_b=gtk_button_new_with_label("ImportMIDI");
-    g_signal_connect (midi_b, "button-press-event", G_CALLBACK(midi_cb), NULL);
-    gtk_grid_attach(GTK_GRID(grid),midi_b,3,row,1,1);
-#endif
-
     row++;
 
 #ifdef GPIO
index 9a81678217f3c10145d5c93d58dd61690849c21e..44876eb3db0ff8e608062950bf5a96afe9cbaa12 100644 (file)
@@ -18,6 +18,7 @@
 */
 
 #include <gtk/gtk.h>
+#include <glib/gprintf.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "receiver.h"
 #include "vfo.h"
 #include "button_text.h"
+#include "actions.h"
 #include "gpio.h"
 #include "i2c.h"
 
-static GtkWidget *parent_window=NULL;
-
-static GtkWidget *dialog=NULL;
-
-static GtkWidget *b_af_gain_rx1;
-static GtkWidget *b_af_gain_rx2;
-static GtkWidget *b_agc_gain_rx1;
-static GtkWidget *b_agc_gain_rx2;
-static GtkWidget *b_attenuation;
-static GtkWidget *b_mic_gain;
-static GtkWidget *b_drive;
-static GtkWidget *b_tune_drive;
-static GtkWidget *b_rit_rx1;
-static GtkWidget *b_rit_rx2;
-static GtkWidget *b_xit;
-static GtkWidget *b_cw_speed;
-static GtkWidget *b_cw_frequency;
-static GtkWidget *b_panadapter_high;
-static GtkWidget *b_panadapter_low;
-static GtkWidget *b_squelch;
-static GtkWidget *b_compression;
-
-static GtkWidget *b_top_af_gain_rx1;
-static GtkWidget *b_top_af_gain_rx2;
-static GtkWidget *b_top_agc_gain_rx1;
-static GtkWidget *b_top_agc_gain_rx2;
-static GtkWidget *b_top_attenuation;
-static GtkWidget *b_top_mic_gain;
-static GtkWidget *b_top_drive;
-static GtkWidget *b_top_tune_drive;
-static GtkWidget *b_top_rit_rx1;
-static GtkWidget *b_top_rit_rx2;
-static GtkWidget *b_top_xit;
-static GtkWidget *b_top_cw_speed;
-static GtkWidget *b_top_cw_frequency;
-static GtkWidget *b_top_panadapter_high;
-static GtkWidget *b_top_panadapter_low;
-static GtkWidget *b_top_squelch;
-static GtkWidget *b_top_compression;
-
-enum {
-  ENC2,
-  ENC2_TOP,
-  ENC2_SW,
-  ENC3,
-  ENC3_TOP,
-  ENC3_SW,
-  ENC4,
-  ENC4_TOP,
-  ENC4_SW,
-  ENC5,
-  ENC5_TOP,
-  ENC5_SW,
-};
-
 typedef struct _choice {
   int id;
   int action;
   GtkWidget *button;
 } CHOICE;
 
-static int encoder;
+static GtkWidget *parent_window=NULL;
+
+static GtkWidget *dialog=NULL;
 
 static void cleanup() {
   if(dialog!=NULL) {
@@ -116,49 +65,36 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d
   return FALSE;
 }
 
-static void enc_select_cb(GtkWidget *widget,gpointer data) {
+static void encoder_bottom_select_cb(GtkWidget *widget,gpointer data) {
   CHOICE *choice=(CHOICE *)data;
-  switch(choice->id) {
-    case ENC2:
-      e2_encoder_action=choice->action;
-      break;
-    case ENC2_TOP:
-      e2_top_encoder_action=choice->action;
-      break;
-    case ENC3:
-      e3_encoder_action=choice->action;
-      break;
-    case ENC3_TOP:
-      e3_top_encoder_action=choice->action;
-      break;
-    case ENC4:
-      e4_encoder_action=choice->action;
-      break;
-    case ENC4_TOP:
-      e4_top_encoder_action=choice->action;
-      break;
-    case ENC5:
-      e5_encoder_action=choice->action;
-      break;
-    case ENC5_TOP:
-      e5_top_encoder_action=choice->action;
-      break;
-  }
+  encoders[choice->id].bottom_encoder_function=choice->action;
+  gtk_button_set_label(GTK_BUTTON(choice->button),encoder_string[choice->action]);
+}
+
+static void encoder_top_select_cb(GtkWidget *widget,gpointer data) {
+  CHOICE *choice=(CHOICE *)data;
+  encoders[choice->id].top_encoder_function=choice->action;
   gtk_button_set_label(GTK_BUTTON(choice->button),encoder_string[choice->action]);
 }
 
-static gboolean enc_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
-  int enc=GPOINTER_TO_INT(data);
+static void encoder_switch_select_cb(GtkWidget *widget,gpointer data) {
+  CHOICE *choice=(CHOICE *)data;
+  encoders[choice->id].switch_function=choice->action;
+  gtk_button_set_label(GTK_BUTTON(choice->button),sw_string[choice->action]);
+}
+
+static gboolean encoder_bottom_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
+  int encoder=GPOINTER_TO_INT(data);
   int i;
 
   GtkWidget *menu=gtk_menu_new();
   for(i=0;i<ENCODER_ACTIONS;i++) {
     GtkWidget *menu_item=gtk_menu_item_new_with_label(encoder_string[i]);
     CHOICE *choice=g_new0(CHOICE,1);
-    choice->id=enc;
+    choice->id=encoder;
     choice->action=i;
     choice->button=widget;
-    g_signal_connect(menu_item,"activate",G_CALLBACK(enc_select_cb),choice);
+    g_signal_connect(menu_item,"activate",G_CALLBACK(encoder_bottom_select_cb),choice);
     gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
   }
   gtk_widget_show_all(menu);
@@ -169,41 +105,46 @@ static gboolean enc_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
 #else
   gtk_menu_popup(GTK_MENU(menu),NULL,NULL,NULL,NULL,0,gtk_get_current_event_time());
 #endif
-  
   return TRUE;
 }
 
-static void sw_select_cb(GtkWidget *widget,gpointer data) {
-  CHOICE *choice=(CHOICE *)data;
-  switch(choice->id) {
-    case ENC2_SW:
-      e2_sw_action=choice->action;
-      break;
-    case ENC3_SW:
-      e3_sw_action=choice->action;
-      break;
-    case ENC4_SW:
-      e4_sw_action=choice->action;
-      break;
-    case ENC5_SW:
-      e5_sw_action=choice->action;
-      break;
+static gboolean encoder_top_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
+  int encoder=GPOINTER_TO_INT(data);
+  int i;
+
+  GtkWidget *menu=gtk_menu_new();
+  for(i=0;i<ENCODER_ACTIONS;i++) {
+    GtkWidget *menu_item=gtk_menu_item_new_with_label(encoder_string[i]);
+    CHOICE *choice=g_new0(CHOICE,1);
+    choice->id=encoder;
+    choice->action=i;
+    choice->button=widget;
+    g_signal_connect(menu_item,"activate",G_CALLBACK(encoder_top_select_cb),choice);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
   }
-  gtk_button_set_label(GTK_BUTTON(choice->button),sw_string[choice->action]);
+  gtk_widget_show_all(menu);
+#if GTK_CHECK_VERSION(3,22,0)
+  gtk_menu_popup_at_pointer(GTK_MENU(menu),(GdkEvent *)event);
+// the following line of code is to work around the problem of the popup menu not having scroll bars.
+  gtk_menu_reposition(GTK_MENU(menu));
+#else
+  gtk_menu_popup(GTK_MENU(menu),NULL,NULL,NULL,NULL,0,gtk_get_current_event_time());
+#endif
+  return TRUE;
 }
 
-static gboolean sw_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
-  int sw=GPOINTER_TO_INT(data);
+static gboolean encoder_switch_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
+  int encoder=GPOINTER_TO_INT(data);
   int i;
 
   GtkWidget *menu=gtk_menu_new();
   for(i=0;i<SWITCH_ACTIONS;i++) {
     GtkWidget *menu_item=gtk_menu_item_new_with_label(sw_string[i]);
     CHOICE *choice=g_new0(CHOICE,1);
-    choice->id=sw;
+    choice->id=encoder;
     choice->action=i;
     choice->button=widget;
-    g_signal_connect(menu_item,"activate",G_CALLBACK(sw_select_cb),choice);
+    g_signal_connect(menu_item,"activate",G_CALLBACK(encoder_switch_select_cb),choice);
     gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
   }
   gtk_widget_show_all(menu);
@@ -214,420 +155,28 @@ static gboolean sw_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
 #else
   gtk_menu_popup(GTK_MENU(menu),NULL,NULL,NULL,NULL,0,gtk_get_current_event_time());
 #endif
-
   return TRUE;
 }
 
-GtkWidget* getRadioButton(int action) {
-  GtkWidget* button;
-  switch(action) {
-    case ENCODER_AF_GAIN_RX1:
-      button=b_af_gain_rx1;
-      break;
-    case ENCODER_AF_GAIN_RX2:
-      button=b_af_gain_rx2;
-      break;
-    case ENCODER_AGC_GAIN_RX1:
-      button=b_agc_gain_rx1;
-      break;
-    case ENCODER_AGC_GAIN_RX2:
-      button=b_agc_gain_rx2;
-      break;
-    case ENCODER_ATTENUATION:
-      button=b_attenuation;
-      break;
-    case ENCODER_MIC_GAIN:
-      button=b_mic_gain;
-      break;
-    case ENCODER_DRIVE:
-      button=b_drive;
-      break;
-    case ENCODER_TUNE_DRIVE:
-      button=b_tune_drive;
-      break;
-    case ENCODER_RIT_RX1:
-      button=b_rit_rx1;
-      break;
-    case ENCODER_RIT_RX2:
-      button=b_rit_rx2;
-      break;
-    case ENCODER_XIT:
-      button=b_xit;
-      break;
-    case ENCODER_CW_SPEED:
-      button=b_cw_speed;
-      break;
-    case ENCODER_CW_FREQUENCY:
-      button=b_cw_frequency;
-      break;
-    case ENCODER_PANADAPTER_HIGH:
-      button=b_panadapter_high;
-      break;
-    case ENCODER_PANADAPTER_LOW:
-      button=b_panadapter_low;
-      break;
-    case ENCODER_SQUELCH:
-      button=b_squelch;
-      break;
-    case ENCODER_COMP:
-      button=b_compression;
-      break;
-  }
-  return button;
-}
-
-GtkWidget* getTopRadioButton(int action) {
-  GtkWidget* button;
-  switch(action) {
-    case ENCODER_AF_GAIN_RX1:
-      button=b_top_af_gain_rx1;
-      break;
-    case ENCODER_AF_GAIN_RX2:
-      button=b_top_af_gain_rx2;
-      break;
-    case ENCODER_AGC_GAIN_RX1:
-      button=b_top_agc_gain_rx1;
-      break;
-    case ENCODER_AGC_GAIN_RX2:
-      button=b_top_agc_gain_rx2;
-      break;
-    case ENCODER_ATTENUATION:
-      button=b_top_attenuation;
-      break;
-    case ENCODER_MIC_GAIN:
-      button=b_top_mic_gain;
-      break;
-    case ENCODER_DRIVE:
-      button=b_top_drive;
-      break;
-/*
-    case ENCODER_TUNE_DRIVE:
-      button=b_top_tune_drive;
-      break;
-*/
-    case ENCODER_RIT_RX1:
-      button=b_top_rit_rx1;
-      break;
-    case ENCODER_RIT_RX2:
-      button=b_top_rit_rx2;
-      break;
-    case ENCODER_XIT:
-      button=b_top_xit;
-      break;
-    case ENCODER_CW_SPEED:
-      button=b_top_cw_speed;
-      break;
-    case ENCODER_CW_FREQUENCY:
-      button=b_top_cw_frequency;
-      break;
-    case ENCODER_PANADAPTER_HIGH:
-      button=b_top_panadapter_high;
-      break;
-    case ENCODER_PANADAPTER_LOW:
-      button=b_top_panadapter_low;
-      break;
-    case ENCODER_SQUELCH:
-      button=b_top_squelch;
-      break;
-    case ENCODER_COMP:
-      button=b_top_compression;
-      break;
-  }
-  return button;
-}
-
-static gboolean select_cb (GtkWidget *widget, gpointer data) {
-  GtkWidget *button;
-  int action;
-
-  switch(encoder) {
-    case 2:
-      action=e2_encoder_action;
-      break;
-    case 3:
-      action=e3_encoder_action;
-      break;
-    case 4:
-      action=e4_encoder_action;
-      break;
-    case 5:
-      action=e5_encoder_action;
-      break;
-  }
-  button=getRadioButton(action);
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
-}
-
-static gboolean top_select_cb (GtkWidget *widget, gpointer data) {
-  GtkWidget *button;
-  int action;
-
-  switch(encoder) {
-    case 2:
-      action=e2_top_encoder_action;
-      break;
-    case 3:
-      action=e3_top_encoder_action;
-      break;
-    case 4:
-      action=e4_top_encoder_action;
-      break;
-    case 5:
-      action=e5_top_encoder_action;
-      break;
-  }
-  button=getTopRadioButton(action);
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
-}
-
-void encoder_select(int pos) {
-  int action;
-  GtkWidget *button;
-  switch(encoder) {
-    case 2:
-        if(pos>0) {
-          e2_encoder_action--;
-          if(e2_encoder_action<0) {
-            e2_encoder_action=ENCODER_ACTIONS-1;
-          }
-        } if(pos<0) {
-          e2_encoder_action++;
-          if(e2_encoder_action>=ENCODER_ACTIONS) {
-            e2_encoder_action=0;
-          }
-        }
-        action=e2_encoder_action;
-      break;
-    case 3:
-        if(pos>0) {
-          e3_encoder_action--;
-          if(e3_encoder_action<0) {
-            e3_encoder_action=ENCODER_ACTIONS-1;
-          }
-        } if(pos<0) {
-          e3_encoder_action++;
-          if(e3_encoder_action>=ENCODER_ACTIONS) {
-            e3_encoder_action=0;
-          }
-        }
-        action=e3_encoder_action;
-      break;
-    case 4:
-        if(pos>0) {
-          e4_encoder_action--;
-          if(e4_encoder_action<0) {
-            e4_encoder_action=ENCODER_ACTIONS-1;
-          }
-        } if(pos<0) {
-          e4_encoder_action++;
-          if(e4_encoder_action>=ENCODER_ACTIONS) {
-            e4_encoder_action=0;
-          }
-        }
-        action=e4_encoder_action;
-      break;
-  }
-
-  button=getRadioButton(action);
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
-
-}
-
-void top_encoder_select(int pos) {
-  int action;
-  GtkWidget *button;
-  switch(encoder) {
-    case 2:
-        if(pos>0) {
-          e2_top_encoder_action--;
-          if(e2_top_encoder_action<0) {
-            e2_top_encoder_action=ENCODER_ACTIONS-1;
-          }
-        } if(pos<0) {
-          e2_top_encoder_action++;
-          if(e2_top_encoder_action>=ENCODER_ACTIONS) {
-            e2_top_encoder_action=0;
-          }
-        }
-        action=e2_top_encoder_action;
-      break;
-    case 3:
-        if(pos>0) {
-          e3_top_encoder_action--;
-          if(e3_top_encoder_action<0) {
-            e3_top_encoder_action=ENCODER_ACTIONS-1;
-          }
-        } if(pos<0) {
-          e3_top_encoder_action++;
-          if(e3_top_encoder_action>=ENCODER_ACTIONS) {
-            e3_top_encoder_action=0;
-          }
-        }
-        action=e3_top_encoder_action;
-      break;
-    case 4:
-        if(pos>0) {
-          e4_top_encoder_action--;
-          if(e4_top_encoder_action<0) {
-            e4_top_encoder_action=ENCODER_ACTIONS-1;
-          }
-        } if(pos<0) {
-          e4_top_encoder_action++;
-          if(e4_top_encoder_action>=ENCODER_ACTIONS) {
-            e4_top_encoder_action=0;
-          }
-        }
-        action=e4_top_encoder_action;
-      break;
-    case 5:
-        if(pos>0) {
-          e5_top_encoder_action--;
-          if(e5_top_encoder_action<0) {
-            e5_top_encoder_action=ENCODER_ACTIONS-1;
-          }
-        } if(pos<0) {
-          e5_top_encoder_action++;
-          if(e5_top_encoder_action>=ENCODER_ACTIONS) {
-            e5_top_encoder_action=0;
-          }
-        }
-        action=e5_top_encoder_action;
-      break;
-  }
-
-  button=getTopRadioButton(action);
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
-
-}
-
-  static gboolean action_select_cb (GtkWidget *widget, gpointer data) {
-    int action=GPOINTER_TO_INT(data);
-    switch(encoder) {
-      case 2:
-        e2_encoder_action=action;
-        break;
-      case 3:
-        e3_encoder_action=action;
-        break;
-      case 4:
-        e4_encoder_action=action;
-        break;
-      case 5:
-        e5_encoder_action=action;
-        break;
-    }
-  }
-
-  static gboolean top_action_select_cb (GtkWidget *widget, gpointer data) {
-    int action=GPOINTER_TO_INT(data);
-    switch(encoder) {
-      case 2:
-        e2_top_encoder_action=action;
-        break;
-      case 3:
-        e3_top_encoder_action=action;
-        break;
-      case 4:
-        e4_top_encoder_action=action;
-        break;
-      case 5:
-        e5_top_encoder_action=action;
-        break;
-    }
-  }
-
-  static gboolean enc2_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e2_encoder_action=i;
-    return TRUE;
-  }
-
-  static gboolean enc2_sw_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e2_sw_action=i;
-    return TRUE;
-  }
-
-  static gboolean enc3_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e3_encoder_action=i;
-    return TRUE;
-  }
-
-  static gboolean enc3_sw_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e3_sw_action=i;
-    return TRUE;
-  }
-
-  static gboolean enc4_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e4_encoder_action=i;
-    return TRUE;
-  }
-
-  static gboolean enc4_sw_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e4_sw_action=i;
-    return TRUE;
-  }
-
-  static gboolean enc2_top_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e2_top_encoder_action=i;
-    return TRUE;
-  }
-
-  static gboolean enc3_top_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e3_top_encoder_action=i;
-    return TRUE;
-  }
-
-  static gboolean enc4_top_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e4_top_encoder_action=i;
-    return TRUE;
-  }
-
-  static gboolean enc5_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e5_encoder_action=i;
-    return TRUE;
-  }
-  static gboolean enc5_sw_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e5_sw_action=i;
-    return TRUE;
-  }
-
-
-  static gboolean enc5_top_cb(GtkWidget *widget, gpointer data) {
-    int i=gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    e5_top_encoder_action=i;
-    return TRUE;
-  }
 
 void encoder_menu(GtkWidget *parent) {
-  int row=0;
-  int col=0;
+  gint row=0;
+  gint col=0;
+  char label[32];
 
   dialog=gtk_dialog_new();
-  gtk_window_set_transient_for(GTK_WINDOW(dialog),GTK_WINDOW(parent_window));
+  gtk_window_set_transient_for(GTK_WINDOW(dialog),GTK_WINDOW(parent));
   //gtk_window_set_decorated(GTK_WINDOW(dialog),FALSE);
   char title[32];
-  sprintf(title,"piHPSDR - Encoder Actions:");
+  sprintf(title,"piHPSDR - Encoder Actions");
   gtk_window_set_title(GTK_WINDOW(dialog),title);
   g_signal_connect (dialog, "delete_event", G_CALLBACK (delete_event), NULL);
 
   GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
-
   GtkWidget *grid=gtk_grid_new();
 
-  gtk_grid_set_column_homogeneous(GTK_GRID(grid),FALSE);
+  gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
   gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
-  gtk_grid_set_column_spacing (GTK_GRID(grid),2);
-  gtk_grid_set_row_spacing (GTK_GRID(grid),2);
 
   GtkWidget *close_b=gtk_button_new_with_label("Close");
   g_signal_connect (close_b, "pressed", G_CALLBACK(close_cb), NULL);
@@ -635,126 +184,80 @@ void encoder_menu(GtkWidget *parent) {
 
   row++;
   col=0;
-  GtkWidget *label_encoder=gtk_label_new(NULL);
-  gtk_label_set_markup(GTK_LABEL(label_encoder), "<b>Encoder</b>");
-  gtk_grid_attach(GTK_GRID(grid),label_encoder,col,row,1,1);
-  col++;
-  if(controller==CONTROLLER2_V2) {
-    GtkWidget *label_bottom=gtk_label_new(NULL);
-    gtk_label_set_markup(GTK_LABEL(label_bottom), "<b>Bottom</b>");
-    gtk_grid_attach(GTK_GRID(grid),label_bottom,col,row,1,1);
-    col++;
-    GtkWidget *label_top=gtk_label_new(NULL);
-    gtk_label_set_markup(GTK_LABEL(label_top), "<b>Top</b>");
-    gtk_grid_attach(GTK_GRID(grid),label_top,col,row,1,1);
-    col++;
-  } else {
-    GtkWidget *label_bottom=gtk_label_new(NULL);
-    gtk_label_set_markup(GTK_LABEL(label_bottom), "<b>Encoder</b>");
-    gtk_grid_attach(GTK_GRID(grid),label_bottom,col,row,1,1);
-    col++;
-  }
-  GtkWidget *label_switch=gtk_label_new(NULL);
-  gtk_label_set_markup(GTK_LABEL(label_switch), "<b>Switch</b>");
-  gtk_grid_attach(GTK_GRID(grid),label_switch,col,row,1,1);
-  col++;
 
-  row++;
-  col=0;
-
-  GtkWidget *enc2_title=gtk_label_new(NULL);
-  gtk_label_set_markup(GTK_LABEL(enc2_title), "<b>ENC2: </b>");
-  gtk_grid_attach(GTK_GRID(grid),enc2_title,col,row,1,1);
-  col++;
-
-  GtkWidget *enc2=gtk_button_new_with_label(encoder_string[e2_encoder_action]);
-  gtk_grid_attach(GTK_GRID(grid),enc2,col,row,1,1);
-  g_signal_connect(enc2,"button_press_event",G_CALLBACK(enc_cb),GINT_TO_POINTER(ENC2));
-  col++;
-  
-  if(controller==CONTROLLER2_V2) {
-    GtkWidget *enc2_top=gtk_button_new_with_label(encoder_string[e2_top_encoder_action]);
-    gtk_grid_attach(GTK_GRID(grid),enc2_top,col,row,1,1);
-    g_signal_connect(enc2_top,"button_press_event",G_CALLBACK(enc_cb),GINT_TO_POINTER(ENC2_TOP));
-    col++;
+  gint max_encoders=MAX_ENCODERS;
+  switch(controller) {
+    case NO_CONTROLLER:
+      max_encoders=0;
+      break;
+    case CONTROLLER1:
+      max_encoders=4;
+      break;
+    case CONTROLLER2_V1:
+      max_encoders=5;
+      break;
+    case CONTROLLER2_V2:
+      max_encoders=5;
+      break;
   }
 
-  GtkWidget *enc2_sw=gtk_button_new_with_label(sw_string[e2_sw_action]);
-  gtk_grid_attach(GTK_GRID(grid),enc2_sw,col,row,1,1);
-  g_signal_connect(enc2_sw,"button_press_event",G_CALLBACK(sw_cb),GINT_TO_POINTER(ENC2_SW));
-
-  row++;
-  col=0;
-
-  GtkWidget *enc3_title=gtk_label_new(NULL);
-  gtk_label_set_markup(GTK_LABEL(enc3_title), "<b>ENC3: </b>");
-  gtk_grid_attach(GTK_GRID(grid),enc3_title,col,row,1,1);
-  col++;
-
-  GtkWidget *enc3=gtk_button_new_with_label(encoder_string[e3_encoder_action]);
-  gtk_grid_attach(GTK_GRID(grid),enc3,col,row,1,1);
-  g_signal_connect(enc3,"button_press_event",G_CALLBACK(enc_cb),GINT_TO_POINTER(ENC3));
+  GtkWidget *widget=gtk_label_new("");
+  gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
   col++;
-  
-  if(controller==CONTROLLER2_V2) {
-    GtkWidget *enc3_top=gtk_button_new_with_label(encoder_string[e3_top_encoder_action]);
-    gtk_grid_attach(GTK_GRID(grid),enc3_top,col,row,1,1);
-    g_signal_connect(enc3_top,"button_press_event",G_CALLBACK(enc_cb),GINT_TO_POINTER(ENC3_TOP));
-    col++;
-  }
-
-  GtkWidget *enc3_sw=gtk_button_new_with_label(sw_string[e3_sw_action]);
-  gtk_grid_attach(GTK_GRID(grid),enc3_sw,col,row,1,1);
-  g_signal_connect(enc3_sw,"button_press_event",G_CALLBACK(sw_cb),GINT_TO_POINTER(ENC3_SW));
-
-  row++;
-  col=0;
 
-  GtkWidget *enc4_title=gtk_label_new(NULL);
-  gtk_label_set_markup(GTK_LABEL(enc4_title), "<b>ENC4: </b>");
-  gtk_grid_attach(GTK_GRID(grid),enc4_title,col,row,1,1);
+  widget=gtk_label_new(NULL);
+  gtk_label_set_markup (GTK_LABEL(widget), controller==CONTROLLER2_V2?"<b>Bottom Encoder</b>":"<b>Encoder</b>");
+  gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
   col++;
 
-  GtkWidget *enc4=gtk_button_new_with_label(encoder_string[e4_encoder_action]);
-  gtk_grid_attach(GTK_GRID(grid),enc4,col,row,1,1);
-  g_signal_connect(enc4,"button_press_event",G_CALLBACK(enc_cb),GINT_TO_POINTER(ENC4));
-  col++;
-  
   if(controller==CONTROLLER2_V2) {
-    GtkWidget *enc4_top=gtk_button_new_with_label(encoder_string[e4_top_encoder_action]);
-    gtk_grid_attach(GTK_GRID(grid),enc4_top,col,row,1,1);
-    g_signal_connect(enc4_top,"button_press_event",G_CALLBACK(enc_cb),GINT_TO_POINTER(ENC4_TOP));
+    widget=gtk_label_new(NULL);
+    gtk_label_set_markup (GTK_LABEL(widget), "<b>Top Encoder</b>");
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
     col++;
   }
 
-  GtkWidget *enc4_sw=gtk_button_new_with_label(sw_string[e4_sw_action]);
-  gtk_grid_attach(GTK_GRID(grid),enc4_sw,col,row,1,1);
-  g_signal_connect(enc4_sw,"button_press_event",G_CALLBACK(sw_cb),GINT_TO_POINTER(ENC4_SW));
+  widget=gtk_label_new(NULL);
+  gtk_label_set_markup (GTK_LABEL(widget), "<b>Switch</b>");
+  gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
 
   row++;
   col=0;
 
-  if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
-    GtkWidget *enc5_title=gtk_label_new(NULL);
-    gtk_label_set_markup(GTK_LABEL(enc5_title), "<b>ENC5: </b>");
-    gtk_grid_attach(GTK_GRID(grid),enc5_title,col,row,1,1);
+  for(int i=0;i<max_encoders;i++) {
+    widget=gtk_label_new(NULL);
+    g_sprintf(label,"<b>%d</b>",i);
+    gtk_label_set_markup (GTK_LABEL(widget), label);
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
     col++;
 
-    GtkWidget *enc5=gtk_button_new_with_label(encoder_string[e5_encoder_action]);
-    gtk_grid_attach(GTK_GRID(grid),enc5,col,row,1,1);
-    g_signal_connect(enc5,"button_press_event",G_CALLBACK(enc_cb),GINT_TO_POINTER(ENC5));
+    if(i==(max_encoders-1)) {
+      widget=gtk_label_new(NULL);
+      g_sprintf(label,"<b>%s</b>",encoder_string[encoders[i].bottom_encoder_function]);
+      gtk_label_set_markup (GTK_LABEL(widget), label);
+    } else {
+      widget=gtk_button_new_with_label(encoder_string[encoders[i].bottom_encoder_function]);
+      g_signal_connect(widget,"button_press_event",G_CALLBACK(encoder_bottom_cb),GINT_TO_POINTER(i));
+    }
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
     col++;
-  
+
     if(controller==CONTROLLER2_V2) {
-      GtkWidget *enc5_top=gtk_button_new_with_label(encoder_string[e5_top_encoder_action]);
-      gtk_grid_attach(GTK_GRID(grid),enc5_top,col,row,1,1);
-      g_signal_connect(enc5_top,"button_press_event",G_CALLBACK(enc_cb),GINT_TO_POINTER(ENC5_TOP));
+      widget=gtk_button_new_with_label(encoder_string[encoders[i].top_encoder_function]);
+      g_signal_connect(widget,"button_press_event",G_CALLBACK(encoder_top_cb),GINT_TO_POINTER(i));
+      gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
       col++;
     }
 
-    GtkWidget *enc5_sw=gtk_button_new_with_label(sw_string[e5_sw_action]);
-    gtk_grid_attach(GTK_GRID(grid),enc5_sw,col,row,1,1);
-    g_signal_connect(enc5_sw,"button_press_event",G_CALLBACK(sw_cb),GINT_TO_POINTER(ENC5_SW));
+    if(i!=(max_encoders-1)) {
+      widget=gtk_button_new_with_label(sw_string[encoders[i].switch_function]);
+      g_signal_connect(widget,"button_press_event",G_CALLBACK(encoder_switch_cb),GINT_TO_POINTER(i));
+      gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+      col++;
+    }
+    
+    row++;
+    col=0;
   }
 
   gtk_container_add(GTK_CONTAINER(content),grid);
diff --git a/ext.c b/ext.c
index 7334a882c28138118b2ea95c7583d0e53789861a..366b2df676aae71c1f7e41bca18f422fc2a09ba1 100644 (file)
--- a/ext.c
+++ b/ext.c
@@ -76,6 +76,8 @@
 // - band_minus(int id)                   // Move VFO #id to next lower band
 // - ctun_update(int id, int state)       // set CTUN state of VFO #id
 // - set_split(int state)                 // Set split mode to state
+// - num_pad(int val)                     // enter VFO frequency
+// - update_vfo_step(int direction)       // cycle throught VFO step sizes
 //
 
 void set_frequency(int v,long long f) {
@@ -160,6 +162,60 @@ void set_split(int val) {
   }
 }
 
+void num_pad(int val) {
+  RECEIVER *rx=active_receiver;
+  if(!vfo[rx->id].entering_frequency) {
+    vfo[rx->id].entered_frequency=0;
+    vfo[rx->id].entering_frequency=TRUE;
+  }
+  switch(val) {
+    case -1: // clear
+      vfo[rx->id].entered_frequency=0;
+      vfo[rx->id].entering_frequency=FALSE;
+      break;
+    case -2: // enter
+      if(vfo[rx->id].entered_frequency!=0) {
+        vfo[rx->id].frequency=vfo[rx->id].entered_frequency;
+        if(vfo[rx->id].ctun) {
+          vfo[rx->id].ctun=FALSE;
+          vfo[rx->id].offset=0;
+          vfo[rx->id].ctun_frequency=vfo[rx->id].frequency;
+        }
+      }
+      vfo[rx->id].entering_frequency=FALSE;
+      break;
+    default:
+      vfo[rx->id].entered_frequency=(vfo[rx->id].entered_frequency*10)+val;
+      break;
+  }
+  vfo_update();
+}
+
+void update_vfo_step(int direction) {
+  int i=0;
+  while(steps[i]!=step && steps[i]!=0) {
+    i++;
+  }
+
+  if(steps[i]!=0) {
+    // current step size is in slot #i.
+    if(direction>0) {
+      // move to next slot (if it exists)
+      i++;
+      if(steps[i]!=0) {
+        step=steps[i];
+      }
+    } else {
+      // move to previous slot (if it exists)
+      i--;
+      if(i>=0) {
+        step=steps[i];
+      }
+    }
+  }
+  vfo_update();
+}
+
 //
 // Functions to be invoked through the GTK idle queue,
 // still in use
diff --git a/ext.h b/ext.h
index 306bf8a5e95019965a1a7c25f05e3990d47d46e7..66c74f97970a78b5880d893ae49661788a18a75f 100644 (file)
--- a/ext.h
+++ b/ext.h
@@ -148,3 +148,5 @@ extern void set_frequency(int v,long long f);
 extern void ctun_update(int id,int state);
 extern void band_plus(int id);
 extern void band_minus(int id);
+extern void num_pad(int num);
+extern void update_vfo_step(int direction);
diff --git a/gpio.c b/gpio.c
index d62e604b501a178137a76735663e7e93b443c2fd..2ef989f7888cdfb75b9a7443a1ece7c714bf772f 100644 (file)
--- a/gpio.c
+++ b/gpio.c
@@ -1,6 +1,6 @@
 /* Copyright (C)
-* 2015 - John Melton, G0ORX/N6LYT
-*
+* 2020 - John Melton, G0ORX/N6LYT
+  *
 * 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
 *
 */
 
+// Rewrite to use gpiod rather than wiringPi
+// Note that all pin numbers are now the Broadcom GPIO
+
+
 #include <gtk/gtk.h>
 
 #include <stdio.h>
 #include <fcntl.h>
 #include <poll.h>
 #include <sched.h>
-#include <wiringPi.h>
-#include <semaphore.h>
+
+#ifdef GPIO
+#include <gpiod.h>
+#include <linux/i2c-dev.h>
+#include <i2c/smbus.h>
+#include <sys/ioctl.h>
+#endif
 
 #include "band.h"
 #include "channel.h"
@@ -38,7 +47,6 @@
 #include "filter.h"
 #include "bandstack.h"
 #include "toolbar.h"
-#include "gpio.h"
 #include "radio.h"
 #include "toolbar.h"
 #include "main.h"
 #include "new_menu.h"
 #include "encoder_menu.h"
 #include "diversity_menu.h"
+#include "actions.h"
 #include "gpio.h"
 #include "i2c.h"
 #include "ext.h"
 #include "sliders.h"
 #include "new_protocol.h"
+#include "zoompan.h"
 #ifdef LOCALCW
 #include "iambic.h"
-#endif
-#include "zoompan.h"
-
-// debounce settle time in ms
-#define DEFAULT_SETTLE_TIME 50
-
-int settle_time=DEFAULT_SETTLE_TIME;
-static gint release_timer=-1;
-
-int controller;
-
-int I2C_INTERRUPT=16;
-
-// uses wiringpi pin numbers
-int ENABLE_VFO_ENCODER;
-int ENABLE_VFO_PULLUP;
-int VFO_ENCODER_A;
-int VFO_ENCODER_B;
-int ENABLE_E2_ENCODER;
-int ENABLE_E2_PULLUP;
-int E2_ENCODER_A;
-int E2_ENCODER_B;
-int ENABLE_E2_TOP_ENCODER;
-int E2_TOP_ENCODER_A;
-int E2_TOP_ENCODER_B;
-int E2_FUNCTION;
-int ENABLE_E3_ENCODER;
-int ENABLE_E3_PULLUP;
-int E3_ENCODER_A;
-int E3_ENCODER_B;
-int ENABLE_E3_TOP_ENCODER;
-int E3_TOP_ENCODER_A;
-int E3_TOP_ENCODER_B;
-int E3_FUNCTION;
-int ENABLE_E4_ENCODER;
-int ENABLE_E4_PULLUP;
-int E4_ENCODER_A;
-int E4_ENCODER_B;
-int ENABLE_E4_TOP_ENCODER;
-int E4_TOP_ENCODER_A;
-int E4_TOP_ENCODER_B;
-int E4_FUNCTION;
-int ENABLE_E5_ENCODER;
-int ENABLE_E5_PULLUP;
-int E5_ENCODER_A;
-int E5_ENCODER_B;
-int ENABLE_E5_TOP_ENCODER;
-int E5_TOP_ENCODER_A;
-int E5_TOP_ENCODER_B;
-int E5_FUNCTION;
-
-int ENABLE_S1_BUTTON;
-int S1_BUTTON;
-int ENABLE_S2_BUTTON;
-int S2_BUTTON;
-int ENABLE_S3_BUTTON;
-int S3_BUTTON;
-int ENABLE_S4_BUTTON;
-int S4_BUTTON;
-int ENABLE_S5_BUTTON;
-int S5_BUTTON;
-int ENABLE_S6_BUTTON;
-int S6_BUTTON;
-int ENABLE_MOX_BUTTON;
-int MOX_BUTTON;
-int ENABLE_FUNCTION_BUTTON;
-int FUNCTION_BUTTON;
 
-#ifdef LOCALCW
 //
-// WiringPi pins #12, 13, 14 are not used
+// Broadcom pins #9, 10, 11 are not used
 // by Controller1 and Controller2_V1
-// (and keep #8,9 reserved for I2C extensions)
+// (and keep #2,3 reserved for I2C extensions)
 //
-int CWL_BUTTON=13;
-int CWR_BUTTON=14;
-int SIDETONE_GPIO=12;
+int CWL_BUTTON=9;
+int CWR_BUTTON=11;
+int SIDETONE_GPIO=10;
 int ENABLE_GPIO_SIDETONE=0;
 int ENABLE_CW_BUTTONS=1;
 int CW_ACTIVE_LOW=1;
@@ -139,2231 +81,1026 @@ int CW_ACTIVE_LOW=1;
 
 #ifdef PTT
 int ENABLE_PTT_GPIO=1;
-int PTT_GPIO=15;
+int PTT_GPIO=14;
 int PTT_ACTIVE_LOW=1;
 #endif
 
-int vfoEncoderPos;
-int vfoFunction;
-
-int e2EncoderPos;
-int e2_sw_action;
-int e2_encoder_action;
-int e3EncoderPos;
-int e3_sw_action;
-int e3_encoder_action;
-int e4EncoderPos;
-int e4_sw_action;
-int e4_encoder_action;
-int e5EncoderPos;
-int e5_sw_action;
-int e5_encoder_action;
-
-int e2TopEncoderPos;
-int e2_top_encoder_action;
-int e3TopEncoderPos;
-int e3_top_encoder_action;
-int e4TopEncoderPos;
-int e4_top_encoder_action;
-int e5TopEncoderPos;
-int e5_top_encoder_action;
-
-static volatile int function_state;
-static volatile int band_state;
-static volatile int bandstack_state;
-static volatile int mode_state;
-static volatile int filter_state;
-static volatile int noise_state;
-static volatile int agc_state;
-static volatile int mox_state;
-static volatile int lock_state;
-
-static gpointer rotary_encoder_thread(gpointer data);
-static GThread *rotary_encoder_thread_id;
-
-static int previous_function_button=0;
-static int band_button=0;
-static int previous_band_button=0;
-static int bandstack_button=0;
-static int previous_bandstack_button=0;
-static int mode_button=0;
-static int previous_mode_button=0;
-static int filter_button=0;
-static int previous_filter_button=0;
-static int noise_button=0;
-static int previous_noise_button=0;
-static int agc_button=0;
-static int previous_agc_button=0;
-static int mox_button=0;
-static int previous_mox_button=0;
-
-static int running=0;
+enum {
+  TOP_ENCODER,
+  BOTTOM_ENCODER
+};
 
-char *encoder_string[ENCODER_ACTIONS] = {
-  "NO ACTION",
-  "AF GAIN",
-  "AF GAIN RX1",
-  "AF GAIN RX2",
-  "AGC GAIN",
-  "AGC GAIN RX1",
-  "AGC GAIN RX2",
-  "ATTENUATION/RX GAIN",
-  "COMP",
-  "CW FREQUENCY",
-  "CW SPEED",
-  "DIVERSITY GAIN",
-  "DIVERSITY GAIN (coarse)",
-  "DIVERSITY GAIN (fine)",
-  "DIVERSITY PHASE",
-  "DIVERSITY PHASE (coarse)",
-  "DIVERSITY PHASE (fine)",
-  "DRIVE",
-  "IF SHIFT",
-  "IF SHIFT RX1",
-  "IF SHIFT RX2",
-  "IF WIDTH",
-  "IF WIDTH RX1",
-  "IF WIDTH RX2",
-  "MIC GAIN",
-  "PAN",
-  "PANADAPTER HIGH",
-  "PANADAPTER LOW",
-  "PANADAPTER STEP",
-  "RF GAIN",
-  "RF GAIN RX1",
-  "RF GAIN RX2",
-  "RIT",
-  "RIT RX1",
-  "RIT RX2",
-  "SQUELCH",
-  "SQUELCH RX1",
-  "SQUELCH RX2",
-  "TUNE DRIVE",
-  "WATERFALL HIGH",
-  "WATERFALL LOW",
-  "XIT",
-  "ZOOM",
+enum {
+  A,
+  B
 };
 
-char *sw_string[SWITCH_ACTIONS] = {
-  "",
-  "A TO B",
-  "A SWAP B",
-  "AGC",
-  "ANF",
-  "B TO A",
-  "BAND -",
-  "BAND +",
-  "BSTACK -",
-  "BSTACK +",
-  "CTUN",
-  "DIV",
-  "FILTER -",
-  "FILTER +",
-  "FUNCTION",
-  "LOCK",
-  "MENU BAND",
-  "MENU BSTACK",
-  "MENU DIV",
-  "MENU FILTER",
-  "MENU FREQUENCY",
-  "MENU MEMORY",
-  "MENU MODE",
-  "MENU PS",
-  "MODE -",
-  "MODE +",
-  "MOX",
-  "MUTE",
-  "NB",
-  "NR",
-  "PAN -",
-  "PAN +",
-  "PS",
-  "RIT",
-  "RIT CL",
-  "SAT",
-  "SNB",
-  "SPLIT",
-  "TUNE",
-  "TWO TONE",
-  "XIT",
-  "XIT CL",
-  "ZOOM -",
-  "ZOOM +",
+// encoder state table
+#define R_START 0x0
+#define R_CW_FINAL 0x1
+#define R_CW_BEGIN 0x2
+#define R_CW_NEXT 0x3
+#define R_CCW_BEGIN 0x4
+#define R_CCW_FINAL 0x5
+#define R_CCW_NEXT 0x6
+
+#define DIR_NONE 0x0
+// Clockwise step.
+#define DIR_CW 0x10
+// Anti-clockwise step.
+#define DIR_CCW 0x20
+
+guchar encoder_state_table[7][4] = {
+  // R_START
+  {R_START,    R_CW_BEGIN,  R_CCW_BEGIN, R_START},
+  // R_CW_FINAL
+  {R_CW_NEXT,  R_START,     R_CW_FINAL,  R_START | DIR_CW},
+  // R_CW_BEGIN
+  {R_CW_NEXT,  R_CW_BEGIN,  R_START,     R_START},
+  // R_CW_NEXT
+  {R_CW_NEXT,  R_CW_BEGIN,  R_CW_FINAL,  R_START},
+  // R_CCW_BEGIN
+  {R_CCW_NEXT, R_START,     R_CCW_BEGIN, R_START},
+  // R_CCW_FINAL
+  {R_CCW_NEXT, R_CCW_FINAL, R_START,     R_START | DIR_CCW},
+  // R_CCW_NEXT
+  {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
 };
 
-int *sw_action=NULL;
+#ifdef GPIO
+char *consumer="pihpsdr";
 
-static int mox_pressed(void *data) {
-  if(running) sim_mox_cb(NULL,NULL);
-  return 0;
-}
+char *gpio_device="/dev/gpiochip0";
 
-static int s1_pressed(void *data) {
-  if(running) sim_s1_pressed_cb(NULL,NULL);
-  return 0;
-}
+static struct gpiod_chip *chip=NULL;
+#endif
 
-static int s1_released(void *data) {
-  if(running) sim_s1_released_cb(NULL,NULL);
-  return 0;
-}
+static GMutex encoder_mutex;
+static GThread *monitor_thread_id;
+
+int I2C_INTERRUPT=15;
+
+#define MAX_LINES 32
+int monitor_lines[MAX_LINES];
+int lines=0;
+
+long settle_time=50;  // ms
+
+// VFO Encoder is always last
+
+ENCODER encoders_no_controller[MAX_ENCODERS]={
+  {FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0L},
+  {FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0L},
+  {FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0L},
+  {FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0L},
+  {FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0L},
+  };
+
+ENCODER encoders_controller1[MAX_ENCODERS]={
+  {TRUE,TRUE,20,1,26,1,0,ENCODER_AF_GAIN,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,TRUE,TRUE,25,MENU_BAND,0L},
+  {TRUE,TRUE,16,1,19,1,0,ENCODER_AGC_GAIN,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,TRUE,TRUE,8,MENU_BANDSTACK,0L},
+  {TRUE,TRUE,4,1,21,1,0,ENCODER_DRIVE,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,TRUE,TRUE,7,MENU_MODE,0L},
+  {TRUE,TRUE,18,1,17,1,0,ENCODER_VFO,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0L},
+  {FALSE,TRUE,0,1,0,0,1,0,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0L},
+  };
+
+ENCODER encoders_controller2_v1[MAX_ENCODERS]={
+  {TRUE,TRUE,20,1,26,1,0,ENCODER_AF_GAIN,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,TRUE,TRUE,22,MENU_BAND,0L},
+  {TRUE,TRUE,4,1,21,1,0,ENCODER_AGC_GAIN,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,TRUE,TRUE,27,MENU_BANDSTACK,0L},
+  {TRUE,TRUE,16,1,19,1,0,ENCODER_IF_WIDTH,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,TRUE,TRUE,23,MENU_MODE,0L},
+  {TRUE,TRUE,25,1,8,1,0,ENCODER_RIT,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,TRUE,TRUE,24,MENU_FREQUENCY,0L},
+  {TRUE,TRUE,18,1,17,1,0,ENCODER_VFO,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0L},
+  };
+
+ENCODER encoders_controller2_v2[MAX_ENCODERS]={
+  {TRUE,TRUE,5,1,6,1,0,ENCODER_RF_GAIN,R_START,TRUE,TRUE,26,1,20,1,0,ENCODER_AF_GAIN,R_START,TRUE,TRUE,22,MENU_BAND,0L},
+  {TRUE,TRUE,9,1,7,1,0,ENCODER_ATTENUATION,R_START,TRUE,TRUE,21,1,4,1,0,ENCODER_AGC_GAIN,R_START,TRUE,TRUE,27,MENU_MODE,0L},
+  {TRUE,TRUE,11,1,10,1,0,ENCODER_IF_WIDTH,R_START,TRUE,TRUE,19,1,16,1,0,ENCODER_IF_SHIFT,R_START,TRUE,TRUE,23,MENU_FILTER,0L},
+  {TRUE,TRUE,13,1,12,1,0,ENCODER_XIT,R_START,TRUE,TRUE,8,1,25,1,0,ENCODER_RIT,R_START,TRUE,TRUE,24,MENU_FREQUENCY,0L},
+  {TRUE,TRUE,18,1,17,1,0,ENCODER_VFO,R_START,FALSE,TRUE,0,0,0,0,0,0,R_START,FALSE,TRUE,0,0,0L},
+  };
+
+ENCODER *encoders=encoders_no_controller;
+
+SWITCH switches_no_controller[MAX_SWITCHES]={
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L},
+  {FALSE,FALSE,0,NO_ACTION,0L}
+  };
+
+SWITCH switches_controller1[MAX_FUNCTIONS][MAX_SWITCHES]={
+  {{TRUE,TRUE,27,MOX,0L},
+   {TRUE,TRUE,13,MENU_BAND,0L},
+   {TRUE,TRUE,12,MENU_BANDSTACK,0L},
+   {TRUE,TRUE,6,MENU_MODE,0L},
+   {TRUE,TRUE,5,MENU_FILTER,0L},
+   {TRUE,TRUE,24,NR,0L},
+   {TRUE,TRUE,23,AGC,0L},
+   {TRUE,TRUE,22,FUNCTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L}},
+  {{TRUE,TRUE,27,MOX,0L},
+   {TRUE,TRUE,13,LOCK,0L},
+   {TRUE,TRUE,12,CTUN,0L},
+   {TRUE,TRUE,6,A_TO_B,0L},
+   {TRUE,TRUE,5,B_TO_A,0L},
+   {TRUE,TRUE,24,A_SWAP_B,0L},
+   {TRUE,TRUE,23,SPLIT,0L},
+   {TRUE,TRUE,22,FUNCTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L}},
+  {{TRUE,TRUE,27,MOX,0L},
+   {TRUE,TRUE,13,MENU_FREQUENCY,0L},
+   {TRUE,TRUE,12,MENU_MEMORY,0L},
+   {TRUE,TRUE,6,RIT,0L},
+   {TRUE,TRUE,5,RIT_PLUS,0L},
+   {TRUE,TRUE,24,RIT_MINUS,0L},
+   {TRUE,TRUE,23,RIT_CLEAR,0L},
+   {TRUE,TRUE,22,FUNCTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L}},
+  {{TRUE,TRUE,27,MOX,0L},
+   {TRUE,TRUE,13,MENU_FREQUENCY,0L},
+   {TRUE,TRUE,12,MENU_MEMORY,0L},
+   {TRUE,TRUE,6,XIT,0L},
+   {TRUE,TRUE,5,XIT_PLUS,0L},
+   {TRUE,TRUE,24,XIT_MINUS,0L},
+   {TRUE,TRUE,23,XIT_CLEAR,0L},
+   {TRUE,TRUE,22,FUNCTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L}},
+  {{TRUE,TRUE,27,MOX,0L},
+   {TRUE,TRUE,13,MENU_FREQUENCY,0L},
+   {TRUE,TRUE,12,SPLIT,0L},
+   {TRUE,TRUE,6,DUPLEX,0L},
+   {TRUE,TRUE,5,SAT,0L},
+   {TRUE,TRUE,24,RSAT,0L},
+   {TRUE,TRUE,23,NO_ACTION,0L},
+   {TRUE,TRUE,22,FUNCTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L}},
+  {{TRUE,TRUE,27,MOX,0L},
+   {TRUE,TRUE,13,TUNE,0L},
+   {TRUE,TRUE,12,TUNE_FULL,0L},
+   {TRUE,TRUE,6,TUNE_MEMORY,0L},
+   {TRUE,TRUE,5,MENU_BAND,0L},
+   {TRUE,TRUE,24,MENU_MODE,0L},
+   {TRUE,TRUE,23,MENU_FILTER,0L},
+   {TRUE,TRUE,22,FUNCTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L},
+   {FALSE,FALSE,0,NO_ACTION,0L}},
+
+  };
+
+SWITCH switches_controller2_v1[MAX_SWITCHES]={
+  {FALSE,FALSE,0,MOX,0L},
+  {FALSE,FALSE,0,TUNE,0L},
+  {FALSE,FALSE,0,PS,0L},
+  {FALSE,FALSE,0,TWO_TONE,0L},
+  {FALSE,FALSE,0,NR,0L},
+  {FALSE,FALSE,0,A_TO_B,0L},
+  {FALSE,FALSE,0,B_TO_A,0L},
+  {FALSE,FALSE,0,MODE_MINUS,0L},
+  {FALSE,FALSE,0,BAND_MINUS,0L},
+  {FALSE,FALSE,0,MODE_PLUS,0L},
+  {FALSE,FALSE,0,BAND_PLUS,0L},
+  {FALSE,FALSE,0,XIT,0L},
+  {FALSE,FALSE,0,NB,0L},
+  {FALSE,FALSE,0,SNB,0L},
+  {FALSE,FALSE,0,LOCK,0L},
+  {FALSE,FALSE,0,CTUN,0L}
+  };
+
+SWITCH switches_controller2_v2[MAX_SWITCHES]={
+  {FALSE,FALSE,0,MOX,0L},
+  {FALSE,FALSE,0,TUNE,0L},
+  {FALSE,FALSE,0,PS,0L},
+  {FALSE,FALSE,0,TWO_TONE,0L},
+  {FALSE,FALSE,0,NR,0L},
+  {FALSE,FALSE,0,A_TO_B,0L},
+  {FALSE,FALSE,0,B_TO_A,0L},
+  {FALSE,FALSE,0,MODE_MINUS,0L},
+  {FALSE,FALSE,0,BAND_MINUS,0L},
+  {FALSE,FALSE,0,MODE_PLUS,0L},
+  {FALSE,FALSE,0,BAND_PLUS,0L},
+  {FALSE,FALSE,0,XIT,0L},
+  {FALSE,FALSE,0,NB,0L},
+  {FALSE,FALSE,0,SNB,0L},
+  {FALSE,FALSE,0,LOCK,0L},
+  {FALSE,FALSE,0,CTUN,0L}
+  };
+
+SWITCH *switches=switches_controller1[0];
 
-static int s2_pressed(void *data) {
-  if(running) sim_s2_pressed_cb(NULL,NULL);
-  return 0;
-}
+static int running=0;
 
-static int s2_released(void *data) {
-  if(running) sim_s2_released_cb(NULL,NULL);
-  return 0;
-}
+static GThread *rotary_encoder_thread_id;
 
-static int s3_pressed(void *data) {
-  if(running) sim_s3_pressed_cb(NULL,NULL);
-  return 0;
-}
+static uint64_t epochMilli;
 
-static int s3_released(void *data) {
-  if(running) sim_s3_released_cb(NULL,NULL);
-  return 0;
-}
+static void initialiseEpoch() {
+  struct timespec ts ;
 
-static int s4_pressed(void *data) {
-  if(running) sim_s4_pressed_cb(NULL,NULL);
-  return 0;
+  clock_gettime (CLOCK_MONOTONIC_RAW, &ts) ;
+  epochMilli = (uint64_t)ts.tv_sec * (uint64_t)1000    + (uint64_t)(ts.tv_nsec / 1000000L) ;
 }
 
-static int s4_released(void *data) {
-  if(running) sim_s4_released_cb(NULL,NULL);
-  return 0;
+static unsigned int millis () {
+  uint64_t now ;
+  struct  timespec ts ;
+  clock_gettime (CLOCK_MONOTONIC_RAW, &ts) ;
+  now  = (uint64_t)ts.tv_sec * (uint64_t)1000 + (uint64_t)(ts.tv_nsec / 1000000L) ;
+  return (uint32_t)(now - epochMilli) ;
 }
 
-static int s5_pressed(void *data) {
-  if(running) sim_s5_pressed_cb(NULL,NULL);
-  return 0;
-}
+static gpointer rotary_encoder_thread(gpointer data) {
+  ENCODER_ACTION *a;
+  int i;
 
-static int s5_released(void *data) {
-  if(running) sim_s5_released_cb(NULL,NULL);
-  return 0;
+  usleep(250000);
+  g_print("%s\n",__FUNCTION__);
+  while(TRUE) {
+    g_mutex_lock(&encoder_mutex);
+    for(i=0;i<MAX_ENCODERS;i++) {
+      if(encoders[i].bottom_encoder_enabled && encoders[i].bottom_encoder_pos!=0) {
+        //g_print("%s: BOTTOM encoder %d pos=%d\n",__FUNCTION__,i,encoders[i].bottom_encoder_pos);
+        a=g_new(ENCODER_ACTION,1);
+        a->action=encoders[i].bottom_encoder_function;
+        a->mode=RELATIVE;
+        a->val=encoders[i].bottom_encoder_pos;
+        g_idle_add(encoder_action,a);
+        encoders[i].bottom_encoder_pos=0;
+      }
+      if(encoders[i].top_encoder_enabled && encoders[i].top_encoder_pos!=0) {
+        //g_print("%s: TOP encoder %d pos=%d\n",__FUNCTION__,i,encoders[i].top_encoder_pos);
+        a=g_new(ENCODER_ACTION,1);
+        a->action=encoders[i].top_encoder_function;
+        a->mode=RELATIVE;
+        a->val=encoders[i].top_encoder_pos;
+        g_idle_add(encoder_action,a);
+        encoders[i].top_encoder_pos=0;
+      }
+    }
+    g_mutex_unlock(&encoder_mutex);
+    usleep(100000); // sleep for 100ms
+  }
 }
 
-static int s6_pressed(void *data) {
-  if(running) sim_s6_pressed_cb(NULL,NULL);
+int process_function_switch(void *data) {
+  function++;
+  if(function>=MAX_FUNCTIONS) {
+    function=0;
+  }
+  switches=switches_controller1[function];
+  update_toolbar_labels();
   return 0;
 }
 
-static int s6_released(void *data) {
-  if(running) sim_s6_released_cb(NULL,NULL);
-  return 0;
-}
+#ifdef GPIO
+static unsigned long switch_debounce;
+
+static void process_encoder(int e,int l,int addr,int val) {
+  guchar pinstate;
+  //g_print("%s: encoder=%d level=%d addr=0x%02X val=%d\n",__FUNCTION__,e,l,addr,val);
+  g_mutex_lock(&encoder_mutex);
+  switch(l) {
+    case BOTTOM_ENCODER:
+      switch(addr) {
+        case A:
+          encoders[e].bottom_encoder_a_value=val;
+          pinstate=(encoders[e].bottom_encoder_b_value<<1) | encoders[e].bottom_encoder_a_value;
+          encoders[e].bottom_encoder_state=encoder_state_table[encoders[e].bottom_encoder_state&0xf][pinstate];
+          //g_print("%s: state=%02X\n",__FUNCTION__,encoders[e].bottom_encoder_state);
+          switch(encoders[e].bottom_encoder_state&0x30) {
+            case DIR_NONE:
+              break;
+            case DIR_CW:
+              encoders[e].bottom_encoder_pos++;
+              break;
+            case DIR_CCW:
+              encoders[e].bottom_encoder_pos--;
+              break;
+            default:
+              break;
+          }
 
-static int function_pressed(void *data) {
-  if(running) sim_function_cb(NULL,NULL);
-  return 0;
+          //g_print("%s: %s BOTTOM pos=%d\n",__FUNCTION__,encoder_string[encoders[e].bottom_encoder_function],encoders[e].bottom_encoder_pos);
+          break;
+        case B:
+          encoders[e].bottom_encoder_b_value=val;
+          pinstate=(encoders[e].bottom_encoder_b_value<<1) | encoders[e].bottom_encoder_a_value;
+          encoders[e].bottom_encoder_state=encoder_state_table[encoders[e].bottom_encoder_state&0xf][pinstate];
+          //g_print("%s: state=%02X\n",__FUNCTION__,encoders[e].bottom_encoder_state);
+          switch(encoders[e].bottom_encoder_state&0x30) {
+            case DIR_NONE:
+              break;
+            case DIR_CW:
+              encoders[e].bottom_encoder_pos++;
+              break;
+            case DIR_CCW:
+              encoders[e].bottom_encoder_pos--;
+              break;
+            default:
+              break;
+          }
+
+          //g_print("%s: %s BOTTOM pos=%d\n",__FUNCTION__,encoder_string[encoders[e].bottom_encoder_function],encoders[e].bottom_encoder_pos);
+
+          break;
+      }
+      break;
+    case TOP_ENCODER:
+      switch(addr) {
+        case A:
+          encoders[e].top_encoder_a_value=val;
+          pinstate=(encoders[e].top_encoder_b_value<<1) | encoders[e].top_encoder_a_value;
+          encoders[e].top_encoder_state=encoder_state_table[encoders[e].top_encoder_state&0xf][pinstate];
+          //g_print("%s: state=%02X\n",__FUNCTION__,encoders[e].top_encoder_state);
+          switch(encoders[e].top_encoder_state&0x30) {
+            case DIR_NONE:
+              break;
+            case DIR_CW:
+              encoders[e].top_encoder_pos++;
+              break;
+            case DIR_CCW:
+              encoders[e].top_encoder_pos--;
+              break;
+            default:
+              break;
+          }
+          //g_print("%s: %s TOP pos=%d\n",__FUNCTION__,encoder_string[encoders[e].top_encoder_function],encoders[e].top_encoder_pos);
+          break;
+        case B:
+          encoders[e].top_encoder_b_value=val;
+          pinstate=(encoders[e].top_encoder_b_value<<1) | encoders[e].top_encoder_a_value;
+          encoders[e].top_encoder_state=encoder_state_table[encoders[e].top_encoder_state&0xf][pinstate];
+          //g_print("%s: state=%02X\n",__FUNCTION__,encoders[e].top_encoder_state);
+          switch(encoders[e].top_encoder_state&0x30) {
+            case DIR_NONE:
+              break;
+            case DIR_CW:
+              encoders[e].top_encoder_pos++;
+              break;
+            case DIR_CCW:
+              encoders[e].top_encoder_pos--;
+              break;
+            default:
+              break;
+          }
+          //g_print("%s: %s TOP pos=%d\n",__FUNCTION__,encoder_string[encoders[e].top_encoder_function],encoders[e].top_encoder_pos);
+
+          break;
+      }
+      break;
+  }
+  g_mutex_unlock(&encoder_mutex);
 }
 
-static int vfo_function_pressed(void *data) {
-  RECEIVER *rx;
-  if(receivers==2) {
-    if(active_receiver==receiver[0]) {
-      rx=receiver[1];
-    } else {
-      rx=receiver[0];
+static void process_edge(int offset,int value) {
+  gint i;
+  gint t;
+  gboolean found;
+
+  //g_print("%s: offset=%d value=%d\n",__FUNCTION__,offset,value);
+  found=FALSE;
+#ifdef LOCALCW
+  if(ENABLE_CW_BUTTONS) {
+    if(offset==CWL_BUTTON) {
+      keyer_event(1, CW_ACTIVE_LOW ? (value==PRESSED) : value);
+      found=TRUE;
+    } else if(offset==CWR_BUTTON) {
+      keyer_event(1, CW_ACTIVE_LOW ? (value==PRESSED) : value);
+      found=TRUE;
+    }
+  }
+  if(found) return;
+#endif
+  // check encoders
+  for(i=0;i<MAX_ENCODERS;i++) {
+    if(encoders[i].bottom_encoder_enabled && encoders[i].bottom_encoder_address_a==offset) {
+      //g_print("%s: found %d encoder %d bottom A\n",__FUNCTION__,offset,i);
+      process_encoder(i,BOTTOM_ENCODER,A,value);
+      found=TRUE;
+      break;
+    } else if(encoders[i].bottom_encoder_enabled && encoders[i].bottom_encoder_address_b==offset) {
+      //g_print("%s: found %d encoder %d bottom B\n",__FUNCTION__,offset,i);
+      process_encoder(i,BOTTOM_ENCODER,B,value);
+      found=TRUE;
+      break;
+    } else if(encoders[i].top_encoder_enabled && encoders[i].top_encoder_address_a==offset) {
+      //g_print("%s: found %d encoder %d top A\n",__FUNCTION__,offset,i);
+      process_encoder(i,TOP_ENCODER,A,value);
+      found=TRUE;
+      break;
+    } else if(encoders[i].top_encoder_enabled && encoders[i].top_encoder_address_b==offset) {
+      //g_print("%s: found %d encoder %d top B\n",__FUNCTION__,offset,i);
+      process_encoder(i,TOP_ENCODER,B,value);
+      found=TRUE;
+      break;
+    } else if(encoders[i].switch_enabled && encoders[i].switch_address==offset) {
+      //g_print("%s: found %d encoder %d switch\n",__FUNCTION__,offset,i);
+      SWITCH_ACTION *a=g_new(SWITCH_ACTION,1);
+      a->action=encoders[i].switch_function;
+      a->state=value;
+      g_idle_add(switch_action,a);
+      found=TRUE;
+      break;
     }
-    active_receiver=rx;
-    g_idle_add(menu_active_receiver_changed,NULL);
-    g_idle_add(ext_vfo_update,NULL);
-    g_idle_add(sliders_active_receiver_changed,NULL);
   }
-  return 0;
-}
 
-static int vfo_function_released(void *data) {
-  return 0;
-}
+  if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
+    if(I2C_INTERRUPT==offset) {
+      if(value==PRESSED) {
+        i2c_interrupt();
+      }
+      found=TRUE;
+    }
+  }
 
-#ifdef PTT
-static int ptt_pressed(void *data) {
-g_print("ptt_pressed\n");
-  if(can_transmit) g_idle_add(ext_mox_update,GINT_TO_POINTER(1));
-  return 0;
-}
+  if(!found) {
+    for(i=0;i<MAX_SWITCHES;i++) {
+      if(switches[i].switch_enabled && switches[i].switch_address==offset) {
+        t=millis();
+        //g_print("%s: found %d switch %d value=%d t=%d\n",__FUNCTION__,offset,i,value,t);
+        found=TRUE;
+        if(t<switches[i].switch_debounce) {
+          return;
+        }
+g_print("%s: switches=%p function=%d (%s)\n",__FUNCTION__,switches,switches[i].switch_function,sw_string[switches[i].switch_function]);
+        switches[i].switch_debounce=t+settle_time;
+        SWITCH_ACTION *a=g_new(SWITCH_ACTION,1);
+        a->action=switches[i].switch_function;
+        a->state=value;
+        g_idle_add(switch_action,a);
+        break;
+      }
+    }
+  }
 
-static int ptt_released(void *data) {
-g_print("ptt_released\n");
-  if(can_transmit) g_idle_add(ext_mox_update,GINT_TO_POINTER(0));
-  return 0;
+
+  if(!found) {
+    g_print("%s: could not find %d\n",__FUNCTION__,offset);
+  }
 }
-#endif
 
-static int e_function_pressed(void *data) {
-  int action=GPOINTER_TO_INT(data);
-g_print("e_function_pressed: %d\n",action);
-  switch(action) {
-    case TUNE:
-      if(can_transmit) g_idle_add(ext_tune_update,NULL);
-      break;
-    case MOX:
-      if(can_transmit) g_idle_add(ext_mox_update,NULL);
-      break;
-    case PS:
-#ifdef PURESIGNAL
-      if(can_transmit) g_idle_add(ext_ps_update,NULL);
-#endif
-      break;
-    case TWO_TONE:
-      if(can_transmit) g_idle_add(ext_two_tone,NULL);
-      break;
-    case NR:
-      g_idle_add(ext_nr_update,NULL);
-      break;
-    case NB:
-      g_idle_add(ext_nb_update,NULL);
-      break;
-    case SNB:
-      g_idle_add(ext_snb_update,NULL);
-      break;
-    case RIT:
-      g_idle_add(ext_rit_update,NULL);
-      break;
-    case RIT_CLEAR:
-      g_idle_add(ext_rit_clear,NULL);
-      break;
-    case XIT:
-      if(can_transmit) g_idle_add(ext_xit_update,NULL);
-      break;
-    case XIT_CLEAR:
-      if(can_transmit) g_idle_add(ext_xit_clear,NULL);
+static int interrupt_cb(int event_type, unsigned int line, const struct timespec *timeout, void* data) {
+  //g_print("%s: event=%d line=%d\n",__FUNCTION__,event_type,line);
+  switch(event_type) {
+    case GPIOD_CTXLESS_EVENT_CB_TIMEOUT:
+      // timeout - ignore
+      //g_print("%s: Ignore timeout\n",__FUNCTION__);
       break;
-    case BAND_PLUS:
-      g_idle_add(ext_band_plus,NULL);
+    case GPIOD_CTXLESS_EVENT_CB_RISING_EDGE:
+      //g_print("%s: Ignore RISING EDGE\n",__FUNCTION__);
+      process_edge(line,RELEASED);
       break;
-    case BAND_MINUS:
-      g_idle_add(ext_band_minus,NULL);
-      break;
-    case BANDSTACK_PLUS:
-      g_idle_add(ext_bandstack_plus,NULL);
-      break;
-    case BANDSTACK_MINUS:
-      g_idle_add(ext_bandstack_minus,NULL);
-      break;
-    case MODE_PLUS:
-      g_idle_add(ext_mode_plus,NULL);
-      break;
-    case MODE_MINUS:
-      g_idle_add(ext_mode_minus,NULL);
-      break;
-    case FILTER_PLUS:
-      g_idle_add(ext_filter_plus,NULL);
-      break;
-    case FILTER_MINUS:
-      g_idle_add(ext_filter_minus,NULL);
-      break;
-    case A_TO_B:
-      g_idle_add(ext_vfo_a_to_b,NULL);
-      break;
-    case B_TO_A:
-      g_idle_add(ext_vfo_b_to_a,NULL);
-      break;
-    case A_SWAP_B:
-      g_idle_add(ext_vfo_a_swap_b,NULL);
-      break;
-    case LOCK:
-      g_idle_add(ext_lock_update,NULL);
-      break;
-    case CTUN:
-      g_idle_add(ext_ctun_update,NULL);
-      break;
-    case AGC:
-      g_idle_add(ext_agc_update,NULL);
-      break;
-    case SPLIT:
-      if(can_transmit) g_idle_add(ext_split_toggle,NULL);
-      break;
-    case DIVERSITY:
-      g_idle_add(ext_diversity_update,GINT_TO_POINTER(0));
-      break;
-    case SAT:
-      if(can_transmit) g_idle_add(ext_sat_update,NULL);
-      break;
-    case MENU_BAND:
-      g_idle_add(ext_band_update,NULL);
-      break;
-    case MENU_BANDSTACK:
-      g_idle_add(ext_bandstack_update,NULL);
-      break;
-    case MENU_MODE:
-      g_idle_add(ext_mode_update,NULL);
-      break;
-    case MENU_FILTER:
-      g_idle_add(ext_filter_update,NULL);
-      break;
-    case MENU_FREQUENCY:
-      g_idle_add(ext_frequency_update,NULL);
-      break;
-    case MENU_MEMORY:
-      g_idle_add(ext_memory_update,NULL);
-      break;
-    case MENU_DIVERSITY:
-      g_idle_add(ext_diversity_update,GINT_TO_POINTER(1));
-      break;
-#ifdef PURESIGNAL
-    case MENU_PS:
-      g_idle_add(ext_start_ps,NULL);
+    case GPIOD_CTXLESS_EVENT_CB_FALLING_EDGE:
+      //g_print("%s: Process FALLING EDGE\n",__FUNCTION__);
+      process_edge(line,PRESSED);
       break;
+  }
+  return GPIOD_CTXLESS_EVENT_CB_RET_OK;
+}
 #endif
-    case FUNCTION:
-      g_idle_add(ext_function_update,NULL);
-      break;
-    case MUTE:
-      g_idle_add(ext_mute_update,NULL);
-      break;
-    case PAN_MINUS:
-      g_idle_add(ext_pan_update,GINT_TO_POINTER(-100));
+
+void gpio_set_defaults(int ctrlr) {
+  int i;
+  g_print("%s: %d\n",__FUNCTION__,ctrlr);
+  switch(ctrlr) {
+    case NO_CONTROLLER:
+      encoders=encoders_no_controller;
+      switches=switches_controller1[0];
       break;
-    case PAN_PLUS:
-      g_idle_add(ext_pan_update,GINT_TO_POINTER(100));
+    case CONTROLLER1:
+      encoders=encoders_controller1;
+      switches=switches_controller1[0];
       break;
-    case ZOOM_MINUS:
-      g_idle_add(ext_zoom_update,GINT_TO_POINTER(-1));
+    case CONTROLLER2_V1:
+      encoders=encoders_controller2_v1;
+      switches=switches_controller2_v1;
       break;
-    case ZOOM_PLUS:
-      g_idle_add(ext_zoom_update,GINT_TO_POINTER(1));
+    case CONTROLLER2_V2:
+      encoders=encoders_controller2_v2;
+      switches=switches_controller2_v2;
       break;
   }
-  return 0;
 }
 
-static unsigned long e2debounce=0;
+void gpio_restore_state() {
+  char* value;
+  char name[80];
 
-static void e2FunctionAlert() {
-    int level=digitalRead(E2_FUNCTION);
-    if(level==0) {
-      if(running) g_idle_add(e_function_pressed,GINT_TO_POINTER(e2_sw_action));
-    }
-}
+  loadProperties("gpio.props");
+  controller=NO_CONTROLLER;
+  value=getProperty("controller");
+  if(value) controller=atoi(value);
+  gpio_set_defaults(controller);
 
-static unsigned long e3debounce=0;
+  for(int i=0;i<MAX_ENCODERS;i++) {
+    sprintf(name,"encoders[%d].bottom_encoder_enabled",i);
+    value=getProperty(name);
+    if(value) encoders[i].bottom_encoder_enabled=atoi(value);
+    sprintf(name,"encoders[%d].bottom_encoder_pullup",i);
+    value=getProperty(name);
+    if(value) encoders[i].bottom_encoder_pullup=atoi(value);
+    sprintf(name,"encoders[%d].bottom_encoder_address_a",i);
+    value=getProperty(name);
+    if(value) encoders[i].bottom_encoder_address_a=atoi(value);
+    sprintf(name,"encoders[%d].bottom_encoder_address_b",i);
+    value=getProperty(name);
+    if(value) encoders[i].bottom_encoder_address_b=atoi(value);
+    sprintf(name,"encoders[%d].bottom_encoder_address_b",i);
+    value=getProperty(name);
+    sprintf(name,"encoders[%d].top_encoder_enabled",i);
+    value=getProperty(name);
+    if(value) encoders[i].top_encoder_enabled=atoi(value);
+    sprintf(name,"encoders[%d].top_encoder_pullup",i);
+    value=getProperty(name);
+    if(value) encoders[i].top_encoder_pullup=atoi(value);
+    sprintf(name,"encoders[%d].top_encoder_address_a",i);
+    value=getProperty(name);
+    if(value) encoders[i].top_encoder_address_a=atoi(value);
+    sprintf(name,"encoders[%d].top_encoder_address_b",i);
+    value=getProperty(name);
+    if(value) encoders[i].top_encoder_address_b=atoi(value);
+    sprintf(name,"encoders[%d].switch_enabled",i);
+    value=getProperty(name);
+    if(value) encoders[i].switch_enabled=atoi(value);
+    sprintf(name,"encoders[%d].switch_pullup",i);
+    value=getProperty(name);
+    if(value) encoders[i].switch_pullup=atoi(value);
+    sprintf(name,"encoders[%d].switch_address",i);
+    value=getProperty(name);
+    if(value) encoders[i].switch_address=atoi(value);
+  }
 
-static void e3FunctionAlert() {
-    int level=digitalRead(E3_FUNCTION);
-    if(level==0) {
-      if(running) g_idle_add(e_function_pressed,GINT_TO_POINTER(e3_sw_action));
+  for(int f=0;f<MAX_FUNCTIONS;f++) {
+    for(int i=0;i<MAX_SWITCHES;i++) {
+      sprintf(name,"switches[%d,%d].switch_enabled",f,i);
+      value=getProperty(name);
+      if(value) switches_controller1[f][i].switch_enabled=atoi(value);
+      sprintf(name,"switches[%d,%d].switch_pullup",f,i);
+      value=getProperty(name);
+      if(value) switches_controller1[f][i].switch_pullup=atoi(value);
+      sprintf(name,"switches[%d,%d].switch_address",f,i);
+      value=getProperty(name);
+      if(value) switches_controller1[f][i].switch_address=atoi(value);
     }
-}
-
-static unsigned long e4debounce=0;
+  }
 
-static void e4FunctionAlert() {
-    int level=digitalRead(E4_FUNCTION);
-    if(level==0) {
-      if(running) g_idle_add(e_function_pressed,GINT_TO_POINTER(e4_sw_action));
+  if(controller!=CONTROLLER1) {
+    for(int i=0;i<MAX_SWITCHES;i++) {
+      sprintf(name,"switches[%d].switch_enabled",i);
+      value=getProperty(name);
+      if(value) switches[i].switch_enabled=atoi(value);
+      sprintf(name,"switches[%d].switch_pullup",i);
+      value=getProperty(name);
+      if(value) switches[i].switch_pullup=atoi(value);
+      sprintf(name,"switches[%d].switch_address",i);
+      value=getProperty(name);
+      if(value) switches[i].switch_address=atoi(value);
     }
-}
-
-static unsigned long e5debounce=0;
+  }
 
-static void e5FunctionAlert() {
-    int level=digitalRead(E5_FUNCTION);
-    if(level==0) {
-      if(running) g_idle_add(e_function_pressed,GINT_TO_POINTER(e5_sw_action));
-    }
 }
 
-static int function_level=1;
-static unsigned long function_debounce=0;
+void gpio_save_state() {
+  char value[80];
+  char name[80];
 
-static void functionAlert() {
-    int t=millis();
-    if(millis()<function_debounce) {
-      return;
-    }
-    int level=digitalRead(FUNCTION_BUTTON);
-    if(level!=function_level) {
-      if(level==0) {
-        if(running) g_idle_add(function_pressed,NULL);
-      }
-      function_level=level;
-      function_debounce=t+settle_time;
-    }
-}
+  clearProperties();
+  sprintf(value,"%d",controller);
+  setProperty("controller",value);
 
-static int s1_level=1;
-static unsigned long s1_debounce=0;
+  for(int i=0;i<MAX_ENCODERS;i++) {
+    sprintf(name,"encoders[%d].bottom_encoder_enabled",i);
+    sprintf(value,"%d",encoders[i].bottom_encoder_enabled);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].bottom_encoder_pullup",i);
+    sprintf(value,"%d",encoders[i].bottom_encoder_pullup);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].bottom_encoder_address_a",i);
+    sprintf(value,"%d",encoders[i].bottom_encoder_address_a);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].bottom_encoder_address_b",i);
+    sprintf(value,"%d",encoders[i].bottom_encoder_address_b);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].bottom_encoder_address_b",i);
+    sprintf(value,"%d",encoders[i].bottom_encoder_address_b);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].top_encoder_enabled",i);
+    sprintf(value,"%d",encoders[i].top_encoder_enabled);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].top_encoder_pullup",i);
+    sprintf(value,"%d",encoders[i].top_encoder_pullup);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].top_encoder_address_a",i);
+    sprintf(value,"%d",encoders[i].top_encoder_address_a);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].top_encoder_address_b",i);
+    sprintf(value,"%d",encoders[i].top_encoder_address_b);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].top_encoder_address_b",i);
+    sprintf(value,"%d",encoders[i].top_encoder_address_b);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].switch_enabled",i);
+    sprintf(value,"%d",encoders[i].switch_enabled);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].switch_pullup",i);
+    sprintf(value,"%d",encoders[i].switch_pullup);
+    setProperty(name,value);
+    sprintf(name,"encoders[%d].switch_address",i);
+    sprintf(value,"%d",encoders[i].switch_address);
+    setProperty(name,value);
+  }
 
-static void s1Alert() {
-    int t=millis();
-    if(millis()<s1_debounce) {
-      return;
+  for(int f=0;f<MAX_FUNCTIONS;f++) {
+    for(int i=0;i<MAX_SWITCHES;i++) {
+      sprintf(name,"switches[%d,%d].switch_enabled",f,i);
+      sprintf(value,"%d",switches_controller1[f][i].switch_enabled);
+      setProperty(name,value);
+      sprintf(name,"switches[%d,%d].switch_pullup",f,i);
+      sprintf(value,"%d",switches_controller1[f][i].switch_pullup);
+      setProperty(name,value);
+      sprintf(name,"switches[%d,%d].switch_address",f,i);
+      sprintf(value,"%d",switches_controller1[f][i].switch_address);
+      setProperty(name,value);
     }
-    int level=digitalRead(S1_BUTTON);
-    if(level!=s1_level) {
-      if(level==0) {
-        if(running) g_idle_add(s1_pressed,NULL);
-      } else {
-        if(running) g_idle_add(s1_released,NULL);
-      }
-      s1_level=level;
-      s1_debounce=t+settle_time;
-    }
-}
-
-static int s2_level=1;
-static unsigned long s2_debounce=0;
+  }
 
-static void s2Alert() {
-    int t=millis();
-    if(millis()<s2_debounce) {
-      return;
-    }
-    int level=digitalRead(S2_BUTTON);
-    if(level!=s2_level) {
-      if(level==0) {
-        if(running) g_idle_add(s2_pressed,NULL);
-      } else {
-        if(running) g_idle_add(s2_released,NULL);
-      }
-      s2_level=level;
-      s2_debounce=t+settle_time;
+  if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
+    for(int i=0;i<MAX_SWITCHES;i++) {
+      sprintf(name,"switches[%d].switch_enabled",i);
+      sprintf(value,"%d",switches[i].switch_enabled);
+      setProperty(name,value);
+      sprintf(name,"switches[%d].switch_pullup",i);
+      sprintf(value,"%d",switches[i].switch_pullup);
+      setProperty(name,value);
+      sprintf(name,"switches[%d].switch_address",i);
+      sprintf(value,"%d",switches[i].switch_address);
+      setProperty(name,value);
     }
-}
-
-static int s3_level=1;
-static unsigned long s3_debounce=0;
+  }
 
-static void s3Alert() {
-    int t=millis();
-    if(millis()<s3_debounce) {
-      return;
-    }
-    int level=digitalRead(S3_BUTTON);
-    if(level!=s3_level) {
-      if(level==0) {
-        if(running) g_idle_add(s3_pressed,NULL);
-      } else {
-        if(running) g_idle_add(s3_released,NULL);
-      }
-      s3_level=level;
-      s3_debounce=t+settle_time;
-    }
+  saveProperties("gpio.props");
 }
 
-static int s4_level=1;
-static unsigned long s4_debounce=0;
-
-static void s4Alert() {
-    int t=millis();
-    if(millis()<s4_debounce) {
-      return;
-    }
-    int level=digitalRead(S4_BUTTON);
-    if(level!=s4_level) {
-      if(level==0) {
-        if(running) g_idle_add(s4_pressed,NULL);
-      } else {
-        if(running) g_idle_add(s4_released,NULL);
-      }
-      s4_level=level;
-      s4_debounce=t+settle_time;
+void gpio_restore_actions() {
+  char name[80];
+  char *value;
+  if(controller!=NO_CONTROLLER) {
+    for(int i=0;i<MAX_ENCODERS;i++) {
+      sprintf(name,"encoders[%d].bottom_encoder_function",i);
+      value=getProperty(name);
+      if(value) encoders[i].bottom_encoder_function=atoi(value);
+      sprintf(name,"encoders[%d].top_encoder_function",i);
+      value=getProperty(name);
+      if(value) encoders[i].top_encoder_function=atoi(value);
+      sprintf(name,"encoders[%d].switch_function",i);
+      value=getProperty(name);
+      if(value) encoders[i].switch_function=atoi(value);
     }
-}
-
-static int s5_level=1;
-static unsigned long s5_debounce=0;
+  }
 
-static void s5Alert() {
-    int t=millis();
-    if(millis()<s5_debounce) {
-      return;
+  for(int f=0;f<MAX_FUNCTIONS;f++) {
+    for(int i=0;i<MAX_SWITCHES;i++) {
+      sprintf(name,"switches[%d,%d].switch_function",f,i);
+      value=getProperty(name);
+      if(value) switches_controller1[f][i].switch_function=atoi(value);
     }
-    int level=digitalRead(S5_BUTTON);
-    if(level!=s5_level) {
-      if(level==0) {
-        if(running) g_idle_add(s5_pressed,NULL);
-      } else {
-        if(running) g_idle_add(s5_released,NULL);
-      }
-      s5_level=level;
-      s5_debounce=t+settle_time;
+  }
+  if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
+    for(int i=0;i<MAX_SWITCHES;i++) {
+      sprintf(name,"switches[%d].switch_function",i);
+      value=getProperty(name);
+      if(value) switches[i].switch_function=atoi(value);
     }
+  }
 }
 
-static int s6_level=1;
-static unsigned long s6_debounce=0;
-
-static void s6Alert() {
-    int t=millis();
-    if(millis()<s6_debounce) {
-      return;
-    }
-    int level=digitalRead(S6_BUTTON);
-    if(level!=s6_level) {
-      if(level==0) {
-        if(running) g_idle_add(s6_pressed,NULL);
-      } else {
-        if(running) g_idle_add(s6_released,NULL);
-      }
-      s6_level=level;
-      s6_debounce=t+settle_time;
+void gpio_save_actions() {
+  char value[80];
+  char name[80];
+  if(controller!=NO_CONTROLLER) {
+    for(int i=0;i<MAX_ENCODERS;i++) {
+      sprintf(name,"encoders[%d].bottom_encoder_function",i);
+      sprintf(value,"%d",encoders[i].bottom_encoder_function);
+      setProperty(name,value);
+      sprintf(name,"encoders[%d].top_encoder_function",i);
+      sprintf(value,"%d",encoders[i].top_encoder_function);
+      setProperty(name,value);
+      sprintf(name,"encoders[%d].switch_function",i);
+      sprintf(value,"%d",encoders[i].switch_function);
+      setProperty(name,value);
     }
-}
-
-static int mox_level=1;
-static unsigned long mox_debounce=0;
+  }
 
-static void moxAlert() {
-    int t=millis();
-    if(millis()<mox_debounce) {
-      return;
+  for(int f=0;f<MAX_FUNCTIONS;f++) {
+    for(int i=0;i<MAX_SWITCHES;i++) {
+      sprintf(name,"switches[%d,%d].switch_function",f,i);
+      sprintf(value,"%d",switches_controller1[f][i].switch_function);
+      setProperty(name,value);
     }
-    int level=digitalRead(MOX_BUTTON);
-    if(level!=mox_level) {
-      if(level==0) {
-        if(running) g_idle_add(mox_pressed,NULL);
-      }
-      mox_level=level;
-      mox_debounce=t+settle_time;
+  }
+  if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
+    for(int i=0;i<MAX_SWITCHES;i++) {
+      sprintf(name,"switches[%d].switch_function",i);
+      sprintf(value,"%d",switches[i].switch_function);
+      setProperty(name,value);
     }
+  }
 }
 
+#ifdef GPIO
+static gpointer monitor_thread(gpointer arg) {
+  struct timespec t;
 
+  // thread to monitor gpio events
+  g_print("%s: start event monitor lines=%d\n",__FUNCTION__,lines);
+  g_print("%s:",__FUNCTION__);
+  for(int i=0;i<lines;i++) {
+    g_print(" %d",monitor_lines[i]);
+  }
+  g_print("\n");
+  t.tv_sec=60;
+  t.tv_nsec=0;
+
+  int ret=gpiod_ctxless_event_monitor_multiple(
+                       gpio_device, GPIOD_CTXLESS_EVENT_BOTH_EDGES,
+                       monitor_lines, lines, FALSE,
+                       consumer, &t, NULL, interrupt_cb,NULL);
+  if (ret<0) {
+    g_print("%s: ctxless event monitor failed: %s\n",__FUNCTION__,g_strerror(errno));
+  }
 
-#ifdef VFO_HAS_FUNCTION
-static unsigned long vfo_debounce=0;
-
-static void vfoFunctionAlert() {
-    if(t-vfo_debounce > settle_time) {
-      int level=digitalRead(VFO_FUNCTION);
-      if(level==0) {
-        if(running) g_idle_add(vfo_function_pressed,NULL);
-      } else {
-        if(running) g_idle_add(vfo_function_released,NULL);
-      }
-      vfo_debounce=t;
-    }
+  g_print("%s: exit\n",__FUNCTION__);
+  return NULL;
 }
-#endif
 
-static void vfoEncoderInt() {
-  static int vfoA=1;
-  int levelA=digitalRead(VFO_ENCODER_A);
-  int levelB=digitalRead(VFO_ENCODER_B);
+static int setup_line(struct gpiod_chip *chip, int offset, gboolean pullup) {
+  int ret;
+  struct gpiod_line_request_config config;
 
-  if(levelA!=vfoA) {
-    if(levelA==levelB) ++vfoEncoderPos;
-    if(levelA!=levelB) --vfoEncoderPos;
-    vfoA=levelA;
+  g_print("%s: %d\n",__FUNCTION__,offset);
+  struct gpiod_line *line=gpiod_chip_get_line(chip, offset);
+  if (!line) {
+    g_print("%s: get line %d failed: %s\n",__FUNCTION__,offset,g_strerror(errno));
+    return -1;
   }
-}
 
-static void e2EncoderInt() {
-  static int e2CurrentA=1;
-  int levelA=digitalRead(E2_ENCODER_A);
-  int levelB=digitalRead(E2_ENCODER_B);
-
-  if(levelA!=e2CurrentA) {
-    if(levelA==levelB) ++e2EncoderPos;
-    if(levelA!=levelB) --e2EncoderPos;
-    e2CurrentA=levelA;
+  config.consumer=consumer;
+  config.request_type=GPIOD_LINE_REQUEST_DIRECTION_INPUT | GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES;
+#ifdef OLD_GPIOD
+  config.flags=pullup?GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW:0;
+#else
+  config.flags=pullup?GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP:GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN;
+#endif
+  ret=gpiod_line_request(line,&config,1);
+  if (ret<0) {
+    g_print("%s: line %d gpiod_line_request failed: %s\n",__FUNCTION__,offset,g_strerror(errno));
+    return ret;
   }
-}
 
-static void e2TopEncoderInt() {
-  static int e2TopCurrentA=1;
-  int levelA=digitalRead(E2_TOP_ENCODER_A);
-  int levelB=digitalRead(E2_TOP_ENCODER_B);
+  gpiod_line_release(line);
 
-  if(levelA!=e2TopCurrentA) {
-    if(levelA==levelB) ++e2TopEncoderPos;
-    if(levelA!=levelB) --e2TopEncoderPos;
-    e2TopCurrentA=levelA;
-  }
+  monitor_lines[lines]=offset;
+  lines++;
+  return 0;
 }
 
-static void e3EncoderInt() {
-  static int e3CurrentA=1;
-  int levelA=digitalRead(E3_ENCODER_A);
-  int levelB=digitalRead(E3_ENCODER_B);
+static int setup_output_line(struct gpiod_chip *chip, int offset, int _initial_value) {
+  int ret;
+  struct gpiod_line_request_config config;
 
-  if(levelA!=e3CurrentA) {
-    if(levelA==levelB) ++e3EncoderPos;
-    if(levelA!=levelB) --e3EncoderPos;
-    e3CurrentA=levelA;
+  g_print("%s: %d\n",__FUNCTION__,offset);
+  struct gpiod_line *line=gpiod_chip_get_line(chip, offset);
+  if (!line) {
+    g_print("%s: get line %d failed: %s\n",__FUNCTION__,offset,g_strerror(errno));
+    return -1;
   }
-}
-
-static void e3TopEncoderInt() {
-  static int e3TopCurrentA=1;
-  int levelA=digitalRead(E3_TOP_ENCODER_A);
-  int levelB=digitalRead(E3_TOP_ENCODER_B);
-
 
-  if(levelA!=e3TopCurrentA) {
-    if(levelA==levelB) ++e3TopEncoderPos;
-    if(levelA!=levelB) --e3TopEncoderPos;
-    e3TopCurrentA=levelA;
+  config.consumer=consumer;
+  config.request_type=GPIOD_LINE_REQUEST_DIRECTION_OUTPUT;
+  ret=gpiod_line_request(line,&config,1);
+  if (ret<0) {
+    g_print("%s: line %d gpiod_line_request failed: %s\n",__FUNCTION__,offset,g_strerror(errno));
+    return ret;
   }
-}
-
-static void e4EncoderInt() {
-  static int e4CurrentA=1;
-  int levelA=digitalRead(E4_ENCODER_A);
-  int levelB=digitalRead(E4_ENCODER_B);
 
-  if(levelA!=e4CurrentA) {
-    if(levelA==levelB) ++e4EncoderPos;
-    if(levelA!=levelB) --e4EncoderPos;
-    e4CurrentA=levelA;
-  }
-}
+  // write initial value
+  
 
-static void e4TopEncoderInt() {
-  static int e4TopCurrentA=1;
-  int levelA=digitalRead(E4_TOP_ENCODER_A);
-  int levelB=digitalRead(E4_TOP_ENCODER_B);
+  gpiod_line_release(line);
 
-  if(levelA!=e4TopCurrentA) {
-    if(levelA==levelB) ++e4TopEncoderPos;
-    if(levelA!=levelB) --e4TopEncoderPos;
-    e4TopCurrentA=levelA;
-  }
+  return 0;
 }
+#endif
 
-static void e5EncoderInt() {
-  static int e5CurrentA=1;
-  int levelA=digitalRead(E5_ENCODER_A);
-  int levelB=digitalRead(E5_ENCODER_B);
+int gpio_init() {
+  int ret=0;
 
+#ifdef GPIO
+  initialiseEpoch();
+  switch_debounce=millis();
 
-  if(levelA!=e5CurrentA) {
-    if(levelA==levelB) ++e5EncoderPos;
-    if(levelA!=levelB) --e5EncoderPos;
-    e5CurrentA=levelA;
-  }
-}
+  g_mutex_init(&encoder_mutex);
+
+  gpio_set_defaults(controller);
 
-static void e5TopEncoderInt() {
-  static int e5TopCurrentA=1;
-  int levelA=digitalRead(E5_TOP_ENCODER_A);
-  int levelB=digitalRead(E5_TOP_ENCODER_B);
+  chip=NULL;
 
-  if(levelA!=e5TopCurrentA) {
-    if(levelA==levelB) ++e5TopEncoderPos;
-    if(levelA!=levelB) --e5TopEncoderPos;
-    e5TopCurrentA=levelA;
+//g_print("%s: open gpio 0\n",__FUNCTION__);
+  chip=gpiod_chip_open_by_number(0);
+  if(chip==NULL) {
+    g_print("%s: open chip failed: %s\n",__FUNCTION__,g_strerror(errno));
+    ret=-1;
+    goto err;
   }
-}
 
-
-static void pI2CInterrupt() {
-    int level=digitalRead(I2C_INTERRUPT);
-    if(level==0) {
-      i2c_interrupt();
+  // setup encoders
+  g_print("%s: setup encoders\n",__FUNCTION__);
+  for(int i=0;i<MAX_ENCODERS;i++) {
+    if(encoders[i].bottom_encoder_enabled) {
+      if(setup_line(chip,encoders[i].bottom_encoder_address_a,encoders[i].bottom_encoder_pullup)<0) {
+        continue;
+      }
+      if(setup_line(chip,encoders[i].bottom_encoder_address_b,encoders[i].bottom_encoder_pullup)<0) {
+        continue;
+      }
     }
-}
 
-#ifdef PTT
-static int ptt_level=1;
-static unsigned long ptt_debounce=0;
+    if(encoders[i].top_encoder_enabled) {
+      if(setup_line(chip,encoders[i].top_encoder_address_a,encoders[i].top_encoder_pullup)<0) {
+        continue;
+      }
+      if(setup_line(chip,encoders[i].top_encoder_address_b,encoders[i].top_encoder_pullup)<0) {
+        continue;
+      }
+    }
 
-static void pttAlert() {
-    int t=millis();
-    if(millis()<ptt_debounce) {
-      return;
+    if(encoders[i].switch_enabled) {
+      if(setup_line(chip,encoders[i].switch_address,encoders[i].switch_pullup)<0) {
+        continue;
+      }
     }
-    int level=digitalRead(PTT_GPIO);
-    if(level!=ptt_level) {
-      if(level==0) {
-        if(running) g_idle_add(ptt_pressed,NULL);
-      } else {
-        if(running) g_idle_add(ptt_released,NULL);
+  }
+
+  // setup switches
+  g_print("%s: setup switches\n",__FUNCTION__);
+  for(int i=0;i<MAX_SWITCHES;i++) {
+    if(switches[i].switch_enabled) {
+      if(setup_line(chip,switches[i].switch_address,switches[i].switch_pullup)<0) {
+        continue;
       }
-      ptt_level=level;
-      ptt_debounce=t+settle_time;
     }
-}
-#endif
+  }
 
-void gpio_set_defaults(int ctrlr) {
-g_print("gpio_set_defaults: %d\n",ctrlr);
-  if(sw_action!=NULL) {
-    g_free(sw_action);
-    sw_action=NULL;
-  }
-  switch(ctrlr) {
-    case NO_CONTROLLER:
-      ENABLE_VFO_ENCODER=0;
-      ENABLE_E2_ENCODER=0;
-      ENABLE_E3_ENCODER=0;
-      ENABLE_E4_ENCODER=0;
-      ENABLE_S1_BUTTON=0;
-      ENABLE_S2_BUTTON=0;
-      ENABLE_S3_BUTTON=0;
-      ENABLE_S4_BUTTON=0;
-      ENABLE_S5_BUTTON=0;
-      ENABLE_S6_BUTTON=0;
-      ENABLE_MOX_BUTTON=0;
-      ENABLE_FUNCTION_BUTTON=0;
-      ENABLE_E5_ENCODER=0;
-      ENABLE_E2_TOP_ENCODER=0;
-      ENABLE_E3_TOP_ENCODER=0;
-      ENABLE_E4_TOP_ENCODER=0;
-      ENABLE_E5_TOP_ENCODER=0;
-      break;
-    case CONTROLLER1:
-      sw_action=g_new(int,CONTROLLER1_SWITCHES);
-      ENABLE_VFO_ENCODER=1;
-      ENABLE_VFO_PULLUP=1;
-      VFO_ENCODER_A=1;
-      VFO_ENCODER_B=0;
-      ENABLE_E2_ENCODER=1;
-      ENABLE_E2_PULLUP=0;
-      E2_ENCODER_A=28;
-      E2_ENCODER_B=25;
-      E2_FUNCTION=6;
-      ENABLE_E3_ENCODER=1;
-      ENABLE_E3_PULLUP=0;
-      E3_ENCODER_A=27;
-      E3_ENCODER_B=24;
-      E3_FUNCTION=10;
-      ENABLE_E4_ENCODER=1;
-      ENABLE_E4_PULLUP=0;
-      E4_ENCODER_A=7;
-      E4_ENCODER_B=29;
-      E4_FUNCTION=11;
-      ENABLE_S1_BUTTON=1;
-      S1_BUTTON=23;
-      ENABLE_S2_BUTTON=1;
-      S2_BUTTON=26;
-      ENABLE_S3_BUTTON=1;
-      S3_BUTTON=22;
-      ENABLE_S4_BUTTON=1;
-      S4_BUTTON=21;
-      ENABLE_S5_BUTTON=1;
-      S5_BUTTON=5;
-      ENABLE_S6_BUTTON=1;
-      S6_BUTTON=4;
-      ENABLE_MOX_BUTTON=1;
-      MOX_BUTTON=2;
-      ENABLE_FUNCTION_BUTTON=1;
-      FUNCTION_BUTTON=3;
-      ENABLE_E5_ENCODER=0;
-      ENABLE_E2_TOP_ENCODER=0;
-      ENABLE_E3_TOP_ENCODER=0;
-      ENABLE_E4_TOP_ENCODER=0;
-      ENABLE_E5_TOP_ENCODER=0;
-      e2_sw_action=MENU_BAND;
-      e2_encoder_action=ENCODER_AF_GAIN;
-      e3_sw_action=MENU_BANDSTACK;
-      e3_encoder_action=ENCODER_AGC_GAIN;
-      e4_sw_action=MENU_MODE;
-      e4_encoder_action=ENCODER_DRIVE;
-      e2_top_encoder_action=ENCODER_NO_ACTION;
-      e3_top_encoder_action=ENCODER_NO_ACTION;
-      e4_top_encoder_action=ENCODER_NO_ACTION;
-      e5_encoder_action=ENCODER_NO_ACTION;
-      e5_top_encoder_action=ENCODER_NO_ACTION;
-      break;
-    case CONTROLLER2_V1:
-      // uses wiringpi pin numbers
-      ENABLE_VFO_ENCODER=1;
-      ENABLE_VFO_PULLUP=1;
-      VFO_ENCODER_A=1;
-      VFO_ENCODER_B=0;
-      ENABLE_E2_ENCODER=1;
-      ENABLE_E2_PULLUP=1;
-      E2_ENCODER_A=28;
-      E2_ENCODER_B=25;
-      E2_FUNCTION=3;
-      ENABLE_E3_ENCODER=1;
-      ENABLE_E3_PULLUP=1;
-      E3_ENCODER_A=7;
-      E3_ENCODER_B=29;
-      E3_FUNCTION=2;
-      ENABLE_E4_ENCODER=1;
-      ENABLE_E4_PULLUP=1;
-      E4_ENCODER_A=27;
-      E4_ENCODER_B=24;
-      E4_FUNCTION=4;
-      ENABLE_E5_ENCODER=1;
-      ENABLE_E5_PULLUP=1;
-      E5_ENCODER_A=6;
-      E5_ENCODER_B=10;
-      E5_FUNCTION=5;
-      ENABLE_S1_BUTTON=0;
-      ENABLE_S2_BUTTON=0;
-      ENABLE_S3_BUTTON=0;
-      ENABLE_S4_BUTTON=0;
-      ENABLE_S5_BUTTON=0;
-      ENABLE_S6_BUTTON=0;
-      ENABLE_E2_TOP_ENCODER=0;
-      ENABLE_E3_TOP_ENCODER=0;
-      ENABLE_E4_TOP_ENCODER=0;
-      ENABLE_E5_TOP_ENCODER=0;
-      sw_action=g_new(int,CONTROLLER2_SWITCHES);
-      sw_action[0]=MOX;
-      sw_action[1]=TUNE;
-      sw_action[2]=PS;
-      sw_action[3]=TWO_TONE;
-      sw_action[4]=NR;
-      sw_action[5]=A_TO_B;
-      sw_action[6]=B_TO_A;
-      sw_action[7]=MODE_MINUS;
-      sw_action[8]=BAND_MINUS;
-      sw_action[9]=MODE_PLUS;
-      sw_action[10]=BAND_PLUS;
-      sw_action[11]=XIT;
-      sw_action[12]=NB;
-      sw_action[13]=SNB;
-      sw_action[14]=LOCK;
-      sw_action[15]=CTUN;
-      e2_sw_action=MENU_BAND;
-      e2_encoder_action=ENCODER_AF_GAIN;
-      e3_sw_action=MENU_BANDSTACK;
-      e3_encoder_action=ENCODER_AGC_GAIN;
-      e4_sw_action=MENU_MODE;
-      e4_encoder_action=ENCODER_IF_WIDTH;
-      e5_sw_action=MENU_FREQUENCY;
-      e5_encoder_action=ENCODER_RIT;
-      e2_top_encoder_action=ENCODER_NO_ACTION;
-      e3_top_encoder_action=ENCODER_NO_ACTION;
-      e4_top_encoder_action=ENCODER_NO_ACTION;
-      e5_top_encoder_action=ENCODER_NO_ACTION;
-      break;
-    case CONTROLLER2_V2:
-      // uses wiringpi pin numbers
-      ENABLE_VFO_ENCODER=1;
-      ENABLE_VFO_PULLUP=0;
-      VFO_ENCODER_A=1;
-      VFO_ENCODER_B=0;
-      ENABLE_E2_ENCODER=1;
-      ENABLE_E2_PULLUP=1;
-      E2_ENCODER_A=21;
-      E2_ENCODER_B=22;
-      ENABLE_E2_TOP_ENCODER=1;
-      E2_TOP_ENCODER_A=25;
-      E2_TOP_ENCODER_B=28;
-      E2_FUNCTION=3;
-      ENABLE_E3_ENCODER=1;
-      ENABLE_E3_PULLUP=1;
-      E3_ENCODER_A=13;
-      E3_ENCODER_B=11;
-      ENABLE_E3_TOP_ENCODER=1;
-      E3_TOP_ENCODER_A=29;
-      E3_TOP_ENCODER_B=7;
-      E3_FUNCTION=2;
-      ENABLE_E4_ENCODER=1;
-      ENABLE_E4_PULLUP=1;
-      E4_ENCODER_A=14;
-      E4_ENCODER_B=12;
-      ENABLE_E4_TOP_ENCODER=1;
-      E4_TOP_ENCODER_A=24;
-      E4_TOP_ENCODER_B=27;
-      E4_FUNCTION=4;
-      ENABLE_E5_ENCODER=1;
-      ENABLE_E5_PULLUP=1;
-      E5_ENCODER_A=23;
-      E5_ENCODER_B=26;
-      ENABLE_E5_TOP_ENCODER=1;
-      E5_TOP_ENCODER_A=10;
-      E5_TOP_ENCODER_B=6;
-      E5_FUNCTION=5;
-      ENABLE_S1_BUTTON=0;
-      ENABLE_S2_BUTTON=0;
-      ENABLE_S3_BUTTON=0;
-      ENABLE_S4_BUTTON=0;
-      ENABLE_S5_BUTTON=0;
-      ENABLE_S6_BUTTON=0;
-      sw_action=g_new(int,CONTROLLER2_SWITCHES);
-      sw_action[0]=MOX;
-      sw_action[1]=TUNE;
-      sw_action[2]=PS;
-      sw_action[3]=TWO_TONE;
-      sw_action[4]=NR;
-      sw_action[5]=A_TO_B;
-      sw_action[6]=B_TO_A;
-      sw_action[7]=MODE_MINUS;
-      sw_action[8]=BAND_MINUS;
-      sw_action[9]=MODE_PLUS;
-      sw_action[10]=BAND_PLUS;
-      sw_action[11]=XIT;
-      sw_action[12]=NB;
-      sw_action[13]=SNB;
-      sw_action[14]=LOCK;
-      sw_action[15]=CTUN;
-      e2_sw_action=MENU_BAND;
-      e2_top_encoder_action=ENCODER_AF_GAIN;
-      e2_encoder_action=ENCODER_RF_GAIN;
-      e3_sw_action=MENU_MODE;
-      e3_top_encoder_action=ENCODER_AGC_GAIN;
-      e3_encoder_action=ENCODER_ATTENUATION;
-      e4_sw_action=MENU_FILTER;
-      e4_top_encoder_action=ENCODER_IF_SHIFT;
-      e4_encoder_action=ENCODER_IF_WIDTH;
-      e5_sw_action=MENU_FREQUENCY;
-      e5_top_encoder_action=ENCODER_RIT;
-      e5_encoder_action=ENCODER_XIT;
-      break;
-    default:
-      break;
-  }
-}
-
-void gpio_restore_actions() {
-  char* value;
-  char name[80];
-  int i;
-
-  if(sw_action!=NULL) {
-    g_free(sw_action);
-    sw_action=NULL;
-  }
-
-  gpio_set_defaults(controller);
-  
-  value=getProperty("settle_time");
-  if(value) settle_time=atoi(value);
-
-  value=getProperty("e2_encoder_action");
-  if(value) e2_encoder_action=atoi(value);
-  value=getProperty("e2_sw_action");
-  if(value) e2_sw_action=atoi(value);
-  value=getProperty("e3_encoder_action");
-  if(value) e3_encoder_action=atoi(value);
-  value=getProperty("e3_sw_action");
-  if(value) e3_sw_action=atoi(value);
-  value=getProperty("e4_encoder_action");
-  if(value) e4_encoder_action=atoi(value);
-  value=getProperty("e4_sw_action");
-  if(value) e4_sw_action=atoi(value);
-  value=getProperty("e2_top_encoder_action");
-  if(value) e2_top_encoder_action=atoi(value);
-  value=getProperty("e3_top_encoder_action");
-  if(value) e3_top_encoder_action=atoi(value);
-  value=getProperty("e4_top_encoder_action");
-  if(value) e4_top_encoder_action=atoi(value);
-  value=getProperty("e5_encoder_action");
-  if(value) e5_encoder_action=atoi(value);
-  value=getProperty("e5_top_encoder_action");
-  if(value) e5_top_encoder_action=atoi(value);
-  value=getProperty("e5_sw_action");
-  if(value) e5_sw_action=atoi(value);
-
-  int switches=0;
-  switch(controller) {
-    case CONTROLLER1:
-      //switches=CONTROLLER1_SWITCHES;
-      switches=0;
-      break;
-    case CONTROLLER2_V1:
-    case CONTROLLER2_V2:
-      switches=CONTROLLER2_SWITCHES;
-      break;
-    default:
-      switches=0;
-      break;
-  }
-
-  for(i=0;i<switches;i++) {
-    sprintf(name,"sw_action[%d]",i);
-    value=getProperty(name);           
-    if(value) sw_action[i]=atoi(value);                
-  }
-}
-
-void gpio_restore_state() {
-  char* value;
-  char name[80];
-
-  loadProperties("gpio.props");
-  value=getProperty("controller");
-  if(value) controller=atoi(value);
-  value=getProperty("ENABLE_VFO_ENCODER");
-  if(value) ENABLE_VFO_ENCODER=atoi(value);
-  value=getProperty("ENABLE_VFO_PULLUP");
-  if(value) ENABLE_VFO_PULLUP=atoi(value);
-  value=getProperty("VFO_ENCODER_A");
-  if(value) VFO_ENCODER_A=atoi(value);
-  value=getProperty("VFO_ENCODER_B");
-  if(value) VFO_ENCODER_B=atoi(value);
-  value=getProperty("ENABLE_E2_ENCODER");
-  if(value) ENABLE_E2_ENCODER=atoi(value);
-  value=getProperty("ENABLE_E2_PULLUP");
-  if(value) ENABLE_E2_PULLUP=atoi(value);
-  value=getProperty("E2_ENCODER_A");
-  if(value) E2_ENCODER_A=atoi(value);
-  value=getProperty("E2_ENCODER_B");
-  if(value) E2_ENCODER_B=atoi(value);
-  value=getProperty("E2_TOP_ENCODER_A");
-  if(value) E2_TOP_ENCODER_A=atoi(value);
-  value=getProperty("E2_TOP_ENCODER_B");
-  if(value) E2_TOP_ENCODER_B=atoi(value);
-  value=getProperty("ENABLE_E3_ENCODER");
-  if(value) ENABLE_E3_ENCODER=atoi(value);
-  value=getProperty("ENABLE_E3_PULLUP");
-  if(value) ENABLE_E3_PULLUP=atoi(value);
-  value=getProperty("E3_ENCODER_A");
-  if(value) E3_ENCODER_A=atoi(value);
-  value=getProperty("E3_ENCODER_B");
-  if(value) E3_ENCODER_B=atoi(value);
-  value=getProperty("E3_TOP_ENCODER_A");
-  if(value) E3_TOP_ENCODER_A=atoi(value);
-  value=getProperty("E3_TOP_ENCODER_B");
-  if(value) E3_TOP_ENCODER_B=atoi(value);
-  value=getProperty("ENABLE_E4_ENCODER");
-  if(value) ENABLE_E4_ENCODER=atoi(value);
-  value=getProperty("ENABLE_E4_PULLUP");
-  if(value) ENABLE_E4_PULLUP=atoi(value);
-  value=getProperty("E4_ENCODER_A");
-  if(value) E4_ENCODER_A=atoi(value);
-  value=getProperty("E4_ENCODER_B");
-  if(value) E4_ENCODER_B=atoi(value);
-  value=getProperty("E4_TOP_ENCODER_A");
-  if(value) E4_TOP_ENCODER_A=atoi(value);
-  value=getProperty("E4_TOP_ENCODER_B");
-  if(value) E4_TOP_ENCODER_B=atoi(value);
-  value=getProperty("ENABLE_E5_ENCODER");
-  if(value) ENABLE_E5_ENCODER=atoi(value);
-  value=getProperty("ENABLE_E5_PULLUP");
-  if(value) ENABLE_E5_PULLUP=atoi(value);
-  value=getProperty("E5_ENCODER_A");
-  if(value) E5_ENCODER_A=atoi(value);
-  value=getProperty("E5_ENCODER_B");
-  if(value) E5_ENCODER_B=atoi(value);
-  value=getProperty("E5_TOP_ENCODER_A");
-  if(value) E5_TOP_ENCODER_A=atoi(value);
-  value=getProperty("E5_TOP_ENCODER_B");
-  if(value) E5_TOP_ENCODER_B=atoi(value);
-  value=getProperty("ENABLE_S1_BUTTON");
-  if(value) ENABLE_S1_BUTTON=atoi(value);
-  value=getProperty("S1_BUTTON");
-  if(value) S1_BUTTON=atoi(value);
-  value=getProperty("ENABLE_S2_BUTTON");
-  if(value) ENABLE_S2_BUTTON=atoi(value);
-  value=getProperty("S2_BUTTON");
-  if(value) S2_BUTTON=atoi(value);
-  value=getProperty("ENABLE_S3_BUTTON");
-  if(value) ENABLE_S3_BUTTON=atoi(value);
-  value=getProperty("S3_BUTTON");
-  if(value) S3_BUTTON=atoi(value);
-  value=getProperty("ENABLE_S4_BUTTON");
-  if(value) ENABLE_S4_BUTTON=atoi(value);
-  value=getProperty("S4_BUTTON");
-  if(value) S4_BUTTON=atoi(value);
-  value=getProperty("ENABLE_S5_BUTTON");
-  if(value) ENABLE_S5_BUTTON=atoi(value);
-  value=getProperty("S5_BUTTON");
-  if(value) S5_BUTTON=atoi(value);
-  value=getProperty("ENABLE_S6_BUTTON");
-  if(value) ENABLE_S6_BUTTON=atoi(value);
-  value=getProperty("S6_BUTTON");
-  if(value) S6_BUTTON=atoi(value);
-  value=getProperty("ENABLE_FUNCTION_BUTTON");
-  if(value) ENABLE_FUNCTION_BUTTON=atoi(value);
-  value=getProperty("FUNCTION_BUTTON");
-  if(value) FUNCTION_BUTTON=atoi(value);
-  value=getProperty("ENABLE_MOX_BUTTON");
-  if(value) ENABLE_MOX_BUTTON=atoi(value);
-  value=getProperty("MOX_BUTTON");
-  if(value) MOX_BUTTON=atoi(value);
-
-  value=getProperty("E2_FUNCTION");
-  if(value) E2_FUNCTION=atoi(value);
-  value=getProperty("E3_FUNCTION");
-  if(value) E3_FUNCTION=atoi(value);
-  value=getProperty("E4_FUNCTION");
-  if(value) E4_FUNCTION=atoi(value);
-  value=getProperty("E5_FUNCTION");
-  if(value) E5_FUNCTION=atoi(value);
-
-#ifdef LOCALCW         
-  value=getProperty("ENABLE_CW_BUTTONS");              
-  if(value) ENABLE_CW_BUTTONS=atoi(value);             
-  value=getProperty("CW_ACTIVE_LOW");          
-  if(value) CW_ACTIVE_LOW=atoi(value);         
-  value=getProperty("CWL_BUTTON");             
-  if(value) CWL_BUTTON=atoi(value);            
-  value=getProperty("CWR_BUTTON");             
-  if(value) CWR_BUTTON=atoi(value);            
-  value=getProperty("SIDETONE_GPIO");          
-  if(value) SIDETONE_GPIO=atoi(value);         
-  value=getProperty("ENABLE_GPIO_SIDETONE");           
-  if(value) ENABLE_GPIO_SIDETONE=atoi(value);          
-#endif
-
-#ifdef PTT
-  switch(controller) {
-    case NO_CONTROLLER:
-      PTT_GPIO=12;
-      break;
-    case CONTROLLER1:
-      PTT_GPIO=12;
-      break;
-    case CONTROLLER2_V1:
-      PTT_GPIO=15;
-      break;
-    case CONTROLLER2_V2:
-      PTT_GPIO=15;
-      break;
-  }
-  value=getProperty("ENABLE_PTT_GPIO");                
-  if(value) ENABLE_PTT_GPIO=atoi(value);               
-  value=getProperty("PTT_GPIO");               
-  if(value) PTT_GPIO=atoi(value);              
-  value=getProperty("PTT_ACTIVE_LOW");         
-  if(value) PTT_ACTIVE_LOW=atoi(value);                
-#endif
-
-  if(controller!=CONTROLLER1) {
-    value=getProperty("i2c_device");
-    if(value) {
-      i2c_device=g_new(char,strlen(value)+1);
-      strcpy(i2c_device,value);
-    }
-    for(int i=0;i<16;i++) {
-      sprintf(name,"i2c_sw[%d]",i);
-      value=getProperty(name);
-      if(value) i2c_sw[i]=atoi(value);
+  if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
+    i2c_init();
+    g_print("%s: setup i2c interrupt %d\n",__FUNCTION__,I2C_INTERRUPT);
+    if((ret=setup_line(chip,I2C_INTERRUPT,TRUE))<0) {
+      goto err;
     }
   }
 
 #ifdef LOCALCW
-  if(controller==CONTROLLER2_V2) {
-    //
-    // In Controller2 V2, no 'free' GPIO pins
-    // are available, so one cannot use any of
-    // of these for CW in the standard setup
-    //
-    ENABLE_CW_BUTTONS=0;
-    ENABLE_GPIO_SIDETONE=0;
-  }
-#endif
-
-
-}
-
-void gpio_save_actions() {
-  int i;
-  char name[80];
-  char value[80];
-
-  sprintf(value,"%d",settle_time);
-  setProperty("settle_time",value);
-  sprintf(value,"%d",e2_sw_action);
-  setProperty("e2_sw_action",value);
-  sprintf(value,"%d",e2_encoder_action);
-  setProperty("e2_encoder_action",value);
-  sprintf(value,"%d",e3_sw_action);
-  setProperty("e3_sw_action",value);
-  sprintf(value,"%d",e3_encoder_action);
-  setProperty("e3_encoder_action",value);
-  sprintf(value,"%d",e4_sw_action);
-  setProperty("e4_sw_action",value);
-  sprintf(value,"%d",e4_encoder_action);
-  setProperty("e4_encoder_action",value);
-  sprintf(value,"%d",e5_sw_action);
-  setProperty("e5_sw_action",value);
-  sprintf(value,"%d",e5_encoder_action);
-  setProperty("e5_encoder_action",value);
-  sprintf(value,"%d",e2_top_encoder_action);
-  setProperty("e2_top_encoder_action",value);
-  sprintf(value,"%d",e3_top_encoder_action);
-  setProperty("e3_top_encoder_action",value);
-  sprintf(value,"%d",e4_top_encoder_action);
-  setProperty("e4_top_encoder_action",value);
-  sprintf(value,"%d",e5_top_encoder_action);
-  setProperty("e5_top_encoder_action",value);
-  int switches=0;
-  switch(controller) {
-    case CONTROLLER1:
-      //switches=CONTROLLER1_SWITCHES;
-      switches=0;
-      break;
-    case CONTROLLER2_V1:
-    case CONTROLLER2_V2:
-      switches=CONTROLLER2_SWITCHES;
-      break;
-    default:
-      switches=0;
-      break;
-  }
-  if(sw_action!=NULL) {
-    for(i=0;i<switches;i++) {
-      sprintf(name,"sw_action[%d]",i);
-      sprintf(value,"%d",sw_action[i]);
-      setProperty(name,value);         
-    }
-  }
-  
-}
-
-void gpio_save_state() {
-  char value[80];
-  char name[80];
-
-  clearProperties();
-  sprintf(value,"%d",controller);
-  setProperty("controller",value);
-  sprintf(value,"%d",ENABLE_VFO_ENCODER);
-  setProperty("ENABLE_VFO_ENCODER",value);
-  sprintf(value,"%d",ENABLE_VFO_PULLUP);
-  setProperty("ENABLE_VFO_PULLUP",value);
-  sprintf(value,"%d",VFO_ENCODER_A);
-  setProperty("VFO_ENCODER_A",value);
-  sprintf(value,"%d",VFO_ENCODER_B);
-  setProperty("VFO_ENCODER_B",value);
-  sprintf(value,"%d",ENABLE_E2_ENCODER);
-  setProperty("ENABLE_E2_ENCODER",value);
-  sprintf(value,"%d",ENABLE_E2_PULLUP);
-  setProperty("ENABLE_E2_PULLUP",value);
-  sprintf(value,"%d",E2_ENCODER_A);
-  setProperty("E2_ENCODER_A",value);
-  sprintf(value,"%d",E2_ENCODER_B);
-  setProperty("E2_ENCODER_B",value);
-  sprintf(value,"%d",E2_TOP_ENCODER_A);
-  setProperty("E2_TOP_ENCODER_A",value);
-  sprintf(value,"%d",E2_TOP_ENCODER_B);
-  setProperty("E2_TOP_ENCODER_B",value);
-  sprintf(value,"%d",ENABLE_E3_ENCODER);
-  setProperty("ENABLE_E3_ENCODER",value);
-  sprintf(value,"%d",ENABLE_E3_PULLUP);
-  setProperty("ENABLE_E3_PULLUP",value);
-  sprintf(value,"%d",E3_ENCODER_A);
-  setProperty("E3_ENCODER_A",value);
-  sprintf(value,"%d",E3_ENCODER_B);
-  setProperty("E3_ENCODER_B",value);
-  sprintf(value,"%d",E3_TOP_ENCODER_A);
-  setProperty("E3_TOP_ENCODER_A",value);
-  sprintf(value,"%d",E3_TOP_ENCODER_B);
-  setProperty("E3_TOP_ENCODER_B",value);
-  sprintf(value,"%d",ENABLE_E4_ENCODER);
-  setProperty("ENABLE_E4_ENCODER",value);
-  sprintf(value,"%d",ENABLE_E4_PULLUP);
-  setProperty("ENABLE_E4_PULLUP",value);
-  sprintf(value,"%d",E4_ENCODER_A);
-  setProperty("E4_ENCODER_A",value);
-  sprintf(value,"%d",E4_ENCODER_B);
-  setProperty("E4_ENCODER_B",value);
-  sprintf(value,"%d",E4_TOP_ENCODER_A);
-  setProperty("E4_TOP_ENCODER_A",value);
-  sprintf(value,"%d",E4_TOP_ENCODER_B);
-  setProperty("E4_TOP_ENCODER_B",value);
-  sprintf(value,"%d",ENABLE_E5_ENCODER);
-  setProperty("ENABLE_E5_ENCODER",value);
-  sprintf(value,"%d",ENABLE_E5_PULLUP);
-  setProperty("ENABLE_E5_PULLUP",value);
-  sprintf(value,"%d",E5_ENCODER_A);
-  setProperty("E5_ENCODER_A",value);
-  sprintf(value,"%d",E5_ENCODER_B);
-  setProperty("E5_ENCODER_B",value);
-  sprintf(value,"%d",E5_TOP_ENCODER_A);
-  setProperty("E5_TOP_ENCODER_A",value);
-  sprintf(value,"%d",E5_TOP_ENCODER_B);
-  setProperty("E5_TOP_ENCODER_B",value);
-  sprintf(value,"%d",ENABLE_S1_BUTTON);
-  setProperty("ENABLE_S1_BUTTON",value);
-  sprintf(value,"%d",S1_BUTTON);
-  setProperty("S1_BUTTON",value);
-  sprintf(value,"%d",ENABLE_S2_BUTTON);
-  setProperty("ENABLE_S2_BUTTON",value);
-  sprintf(value,"%d",S2_BUTTON);
-  setProperty("S2_BUTTON",value);
-  sprintf(value,"%d",ENABLE_S3_BUTTON);
-  setProperty("ENABLE_S3_BUTTON",value);
-  sprintf(value,"%d",S3_BUTTON);
-  setProperty("S3_BUTTON",value);
-  sprintf(value,"%d",ENABLE_S4_BUTTON);
-  setProperty("ENABLE_S4_BUTTON",value);
-  sprintf(value,"%d",S4_BUTTON);
-  setProperty("S4_BUTTON",value);
-  sprintf(value,"%d",ENABLE_S5_BUTTON);
-  setProperty("ENABLE_S5_BUTTON",value);
-  sprintf(value,"%d",S5_BUTTON);
-  setProperty("S5_BUTTON",value);
-  sprintf(value,"%d",ENABLE_S6_BUTTON);
-  setProperty("ENABLE_S6_BUTTON",value);
-  sprintf(value,"%d",S6_BUTTON);
-  setProperty("S6_BUTTON",value);
-  sprintf(value,"%d",ENABLE_FUNCTION_BUTTON);
-  setProperty("ENABLE_FUNCTION_BUTTON",value);
-  sprintf(value,"%d",FUNCTION_BUTTON);
-  setProperty("FUNCTION_BUTTON",value);
-  sprintf(value,"%d",ENABLE_MOX_BUTTON);
-  setProperty("ENABLE_MOX_BUTTON",value);
-  sprintf(value,"%d",MOX_BUTTON);
-  setProperty("MOX_BUTTON",value);
-
-  sprintf(value,"%d",E2_FUNCTION);
-  setProperty("E2_FUNCTION",value);
-  sprintf(value,"%d",E3_FUNCTION);
-  setProperty("E3_FUNCTION",value);
-  sprintf(value,"%d",E4_FUNCTION);
-  setProperty("E4_FUNCTION",value);
-  sprintf(value,"%d",E5_FUNCTION);
-  setProperty("E5_FUNCTION",value);
-
-  if(controller!=CONTROLLER1) {
-    setProperty("i2c_device",i2c_device);              
-    sprintf(value,"%ud",i2c_address_1);
-    setProperty("i2c_address_1",value);                
-    for(int i=0;i<16;i++) {
-      sprintf(name,"i2c_sw[%d]",i);
-      sprintf(value,"%ud",i2c_sw[i]);
-      setProperty(name,value);         
+  g_print("%s: ENABLE_CW_BUTTONS=%d  CWL_BUTTON=%d CWR_BUTTON=%d\n", __FUNCTION__, ENABLE_CW_BUTTONS, CWL_BUTTON, CWR_BUTTON);
+  if(ENABLE_CW_BUTTONS) {
+    if((ret=setup_line(chip,CWL_BUTTON,CW_ACTIVE_LOW==1))<0) {
+      goto err;
     }
-
-  }
-
-#ifdef LOCALCW         
-  sprintf(value,"%d",ENABLE_CW_BUTTONS);               
-  setProperty("ENABLE_CW_BUTTONS",value);              
-  sprintf(value,"%d",CW_ACTIVE_LOW);           
-  setProperty("CW_ACTIVE_LOW",value);          
-  sprintf(value,"%d",CWL_BUTTON);              
-  setProperty("CWL_BUTTON",value);             
-  sprintf(value,"%d",CWR_BUTTON);              
-  setProperty("CWR_BUTTON",value);             
-  sprintf(value,"%d",SIDETONE_GPIO);           
-  setProperty("SIDETONE_GPIO",value);          
-  sprintf(value,"%d",ENABLE_GPIO_SIDETONE);            
-  setProperty("ENABLE_GPIO_SIDETONE",value);           
-#endif
-
-#ifdef PTT
-  sprintf(value,"%d",ENABLE_PTT_GPIO);         
-  setProperty("ENABLE_PTT_GPIO",value);                
-  sprintf(value,"%d",PTT_GPIO);                
-  setProperty("PTT_GPIO",value);               
-  sprintf(value,"%d",PTT_ACTIVE_LOW);          
-  setProperty("PTT_ACTIVE_LOW",value);         
-#endif
-  saveProperties("gpio.props");
-
-}
-
-static void setup_pin(int pin, int up_down, void(*pAlert)(void)) {
-  int rc;
-g_print("setup_pin: pin=%d up_down=%d\n",pin,up_down);
-  pinMode(pin,GPIO);
-  pinMode(pin,INPUT);
-  pullUpDnControl(pin,up_down);
-  usleep(10000);
-  rc=wiringPiISR(pin,INT_EDGE_BOTH,pAlert);
-  if(rc<0) {
-    fprintf(stderr,"wirngPiISR returned %d\n",rc);
-  }
-}
-
-
-static void setup_encoder_pin(int pin, int up_down, void(*pAlert)(void)) {
-  int rc;
-  pinMode(pin,GPIO);
-  pinMode(pin,INPUT);
-  pullUpDnControl(pin,up_down);
-  usleep(10000);
-  if(pAlert!=NULL) {
-    rc=wiringPiISR(pin,INT_EDGE_BOTH,pAlert);
-    if(rc<0) {
-      fprintf(stderr,"wirngPiISR returned %d\n",rc);
+    monitor_lines[lines]=CWL_BUTTON;
+    lines++;
+    if((ret=setup_line(chip,CWR_BUTTON,CW_ACTIVE_LOW==1))<0) {
+      goto err;
     }
-  }
-}
-
-#ifdef LOCALCW
-
-//
-// We generate interrupts only on falling edge
-//
+    monitor_lines[lines]=CWR_BUTTON;
+    lines++;
 
-static void setup_cw_pin(int pin, void(*pAlert)(void)) {
-  int rc;
-  pinMode(pin,INPUT);
-  pullUpDnControl(pin,PUD_UP);
-  usleep(10000);
-  rc=wiringPiISR(pin,INT_EDGE_BOTH,pAlert);
-  if(rc<0) {
-    fprintf(stderr,"wirngPiISR returned %d\n",rc);
   }
-}
-
-static void cwAlert_left() {
-    int level;
-    if (cw_keyer_internal != 0) return; // as quickly as possible
-    level=digitalRead(CWL_BUTTON);
-    //fprintf(stderr,"cwl button : level=%d \n",level);
-    //the second parameter of keyer_event ("state") is TRUE on key-down
-    keyer_event(1, CW_ACTIVE_LOW ? (level==0) : level);
-}
-
-static void cwAlert_right() {
-    int level;
-    if (cw_keyer_internal != 0) return; // as quickly as possible
-    level=digitalRead(CWR_BUTTON);
-    //fprintf(stderr,"cwr button : level=%d \n",level);
-     keyer_event(0, CW_ACTIVE_LOW ? (level==0) : level);
-}
-
+  if (ENABLE_GPIO_SIDETONE) {
 //
-// The following functions are an interface for
-// other parts to access CW gpio functions
-// (query left and right paddle, set sidetone output)
+//  use this pin as an output pin and
+//  set its value to LOW
 //
-int gpio_left_cw_key() {
-  int val=digitalRead(CWL_BUTTON);
-  return CW_ACTIVE_LOW? (val==0) : val;
-}
-
-int gpio_right_cw_key() {
-  int val=digitalRead(CWR_BUTTON);
-  return CW_ACTIVE_LOW? (val==0) : val;
-}
-
-int gpio_cw_sidetone_enabled() {
-  return ENABLE_GPIO_SIDETONE;
-}
-
-void gpio_cw_sidetone_set(int level) {
-  if (ENABLE_GPIO_SIDETONE) {
-    digitalWrite(SIDETONE_GPIO, level);
+    if((ret=setup_output_line(chip,SIDETONE_GPIO,0))<0) {
+      goto err;
+    }
   }
-}
 #endif
 
-int gpio_init() {
-
-  fprintf(stderr,"gpio_wiringpi: gpio_init\n");
-
-  gpio_restore_state();
-
-  wiringPiSetup(); // use WiringPi pin numbers
-
-  if(ENABLE_VFO_ENCODER) {
-#ifdef VFO_HAS_FUNCTION
-    setup_pin(VFO_FUNCTION, PUD_UP, &vfoFunctionAlert);
-    vfoFunction=0;
+  if(controller!=NO_CONTROLLER
+#ifdef LOCALCW
+    || ENABLE_CW_BUTTONS
 #endif
-    setup_encoder_pin(VFO_ENCODER_A,ENABLE_VFO_PULLUP?PUD_UP:PUD_DOWN,&vfoEncoderInt);
-    setup_encoder_pin(VFO_ENCODER_B,ENABLE_VFO_PULLUP?PUD_UP:PUD_DOWN,NULL);
-    vfoEncoderPos=0;
-  }
-
-  if(ENABLE_E2_ENCODER) {
-    setup_pin(E2_FUNCTION, PUD_UP, &e2FunctionAlert);
-         
-    setup_encoder_pin(E2_ENCODER_A,ENABLE_E2_PULLUP?PUD_UP:PUD_OFF,&e2EncoderInt);
-    setup_encoder_pin(E2_ENCODER_B,ENABLE_E2_PULLUP?PUD_UP:PUD_OFF,NULL);
-    e2EncoderPos=0;
-
-
-    if(controller==CONTROLLER2_V2) {
-      setup_encoder_pin(E2_TOP_ENCODER_A,ENABLE_E2_PULLUP?PUD_UP:PUD_OFF,&e2TopEncoderInt);
-      setup_encoder_pin(E2_TOP_ENCODER_B,ENABLE_E2_PULLUP?PUD_UP:PUD_OFF,NULL);
-      e2TopEncoderPos=0;
+    ) {
+    monitor_thread_id = g_thread_new( "gpiod monitor", monitor_thread, NULL);
+    if(!monitor_thread_id ) {
+      g_print("%s: g_thread_new failed for monitor_thread\n",__FUNCTION__);
     }
-  }
-
-  if(ENABLE_E3_ENCODER) {
-    setup_pin(E3_FUNCTION, PUD_UP, &e3FunctionAlert);
-       
-    setup_encoder_pin(E3_ENCODER_A,ENABLE_E3_PULLUP?PUD_UP:PUD_OFF,&e3EncoderInt);
-    setup_encoder_pin(E3_ENCODER_B,ENABLE_E3_PULLUP?PUD_UP:PUD_OFF,NULL);
-    e3EncoderPos=0;
-
-    if(controller==CONTROLLER2_V2) {
-      setup_encoder_pin(E3_TOP_ENCODER_A,ENABLE_E3_PULLUP?PUD_UP:PUD_OFF,&e3TopEncoderInt);
-      setup_encoder_pin(E3_TOP_ENCODER_B,ENABLE_E3_PULLUP?PUD_UP:PUD_OFF,NULL);
-      e3TopEncoderPos=0;
-    }
-  }
-
-
-  if(ENABLE_E4_ENCODER) {
-    setup_pin(E4_FUNCTION, PUD_UP, &e4FunctionAlert);
-         
-    setup_encoder_pin(E4_ENCODER_A,ENABLE_E4_PULLUP?PUD_UP:PUD_OFF,&e4EncoderInt);
-    setup_encoder_pin(E4_ENCODER_B,ENABLE_E4_PULLUP?PUD_UP:PUD_OFF,NULL);
-    e4EncoderPos=0;
-         
-    if(controller==CONTROLLER2_V2) {
-      setup_encoder_pin(E4_TOP_ENCODER_A,ENABLE_E4_PULLUP?PUD_UP:PUD_OFF,&e4TopEncoderInt);
-      setup_encoder_pin(E4_TOP_ENCODER_B,ENABLE_E4_PULLUP?PUD_UP:PUD_OFF,NULL);
-      e4TopEncoderPos=0;
-    }
-  }
-
-  if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
-    if(ENABLE_E5_ENCODER) {
-      setup_pin(E5_FUNCTION, PUD_UP, &e5FunctionAlert);
-
-      setup_encoder_pin(E5_ENCODER_A,ENABLE_E5_PULLUP?PUD_UP:PUD_OFF,&e5EncoderInt);
-      setup_encoder_pin(E5_ENCODER_B,ENABLE_E5_PULLUP?PUD_UP:PUD_OFF,NULL);
-      e5EncoderPos=0;
 
-      if(controller==CONTROLLER2_V2) {
-        setup_encoder_pin(E5_TOP_ENCODER_A,ENABLE_E5_PULLUP?PUD_UP:PUD_OFF,&e5TopEncoderInt);
-        setup_encoder_pin(E5_TOP_ENCODER_B,ENABLE_E5_PULLUP?PUD_UP:PUD_OFF,NULL);
-        e5TopEncoderPos=0;
+    if(controller!=NO_CONTROLLER) {
+      rotary_encoder_thread_id = g_thread_new( "encoders", rotary_encoder_thread, NULL);
+      if(!rotary_encoder_thread_id ) {
+        g_print("%s: g_thread_new failed on rotary_encoder_thread\n",__FUNCTION__);
+        exit( -1 );
       }
+      g_print("%s: rotary_encoder_thread: id=%p\n",__FUNCTION__,rotary_encoder_thread_id);
     }
   }
 
-  if(controller==CONTROLLER1){
-    if(ENABLE_FUNCTION_BUTTON) {
-      setup_pin(FUNCTION_BUTTON, PUD_UP, &functionAlert);
-    }
-  
-    if(ENABLE_MOX_BUTTON) {
-      setup_pin(MOX_BUTTON, PUD_UP, &moxAlert);
-    }
-  
-    if(ENABLE_S1_BUTTON) {
-      setup_pin(S1_BUTTON, PUD_UP, &s1Alert);
-    }
-  
-    if(ENABLE_S2_BUTTON) {
-      setup_pin(S2_BUTTON, PUD_UP, &s2Alert);
-    }
-  
-    if(ENABLE_S3_BUTTON) {
-      setup_pin(S3_BUTTON, PUD_UP, &s3Alert);
-    }
-  
-    if(ENABLE_S4_BUTTON) {
-      setup_pin(S4_BUTTON, PUD_UP, &s4Alert);
-    }
-  
-    if(ENABLE_S5_BUTTON) {
-      setup_pin(S5_BUTTON, PUD_UP, &s5Alert);
-    }
-  
-    if(ENABLE_S6_BUTTON) {
-      setup_pin(S6_BUTTON, PUD_UP, &s6Alert);
-    }
-  }
-
-  rotary_encoder_thread_id = g_thread_new( "encoders", rotary_encoder_thread, NULL);
-  if( ! rotary_encoder_thread_id )
-  {
-    fprintf(stderr,"g_thread_new failed on rotary_encoder_thread\n");
-    exit( -1 );
-  }
-  fprintf(stderr, "rotary_encoder_thread: id=%p\n",rotary_encoder_thread_id);
-
-  if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
-    // setup i2c
-    i2c_init();
-
-    // setup interrupt pin
-    fprintf(stderr,"setup i2c interrupt: pin=%d\n",I2C_INTERRUPT);
-    pinMode(I2C_INTERRUPT,INPUT);
-    pullUpDnControl(I2C_INTERRUPT,PUD_UP);
-    usleep(10000);
-    wiringPiISR(I2C_INTERRUPT,INT_EDGE_BOTH,pI2CInterrupt);
-  }
-
-#ifdef LOCALCW
-  fprintf(stderr,"GPIO: ENABLE_CW_BUTTONS=%d  CWL_BUTTON=%d CWR_BUTTON=%d\n", ENABLE_CW_BUTTONS, CWL_BUTTON, CWR_BUTTON);
-  if(ENABLE_CW_BUTTONS) {      
-    setup_cw_pin(CWL_BUTTON, cwAlert_left);
-    setup_cw_pin(CWR_BUTTON, cwAlert_right);
-  }
-  if (ENABLE_GPIO_SIDETONE) {
-//
-//  use this pin as an output pin and
-//  set its value to LOW
-//
-    pinMode(SIDETONE_GPIO, OUTPUT);
-    digitalWrite(SIDETONE_GPIO, 0);
-  }
 #endif
+  return 0;
 
-#ifdef PTT
-  if(ENABLE_PTT_GPIO) {
-g_print("PTT Enabled: setup pin %d active_low=%d\n",PTT_GPIO,PTT_ACTIVE_LOW);
-    setup_pin(PTT_GPIO,PTT_ACTIVE_LOW?PUD_UP:PUD_DOWN,pttAlert);
+err:
+g_print("%s: err\n",__FUNCTION__);
+#ifdef GPIO
+  if(chip!=NULL) {
+    gpiod_chip_close(chip);
+    chip=NULL;
   }
 #endif
-
-  return 0;
+  return ret;
 }
 
 void gpio_close() {
-    running=0;
+#ifdef GPIO
+  if(chip!=NULL) gpiod_chip_close(chip);
+#endif
 }
 
-int vfo_encoder_get_pos() {
-  int pos=vfoEncoderPos;
-
-  if(vfo_encoder_divisor>1) {
-    if(pos<0 && pos>-vfo_encoder_divisor) {
-        pos=0;
-    } else if(pos>0 && pos<vfo_encoder_divisor) {
-        pos=0;
+#ifdef LOCALCW
+void gpio_cw_sidetone_set(int level) {
+  int rc;
+#ifdef GPIO
+  if (ENABLE_GPIO_SIDETONE) {
+    if((rc=gpiod_ctxless_set_value_ext(gpio_device,SIDETONE_GPIO,level,FALSE,consumer,NULL,NULL,0))<0) {
+      g_print("%s: err=%d\n",__FUNCTION__,rc);
     }
-    pos=pos/vfo_encoder_divisor;
-    vfoEncoderPos=vfoEncoderPos-(pos*vfo_encoder_divisor);
-  } else {
-    vfoEncoderPos=0;
   }
-  return pos;
-}
-
-int e2_encoder_get_pos() {
-    int pos=e2EncoderPos;
-    e2EncoderPos=0;
-    return pos;
-}
-
-int e3_encoder_get_pos() {
-    int pos=e3EncoderPos;
-    e3EncoderPos=0;
-    return pos;
-}
-
-int e4_encoder_get_pos() {
-    int pos=e4EncoderPos;
-    e4EncoderPos=0;
-    return pos;
-}
-
-int e5_encoder_get_pos() {
-    int pos=e5EncoderPos;
-    e5EncoderPos=0;
-    return pos;
-}
-
-int e4_function_get_state() {
-    return e4_sw_action;
-}
-
-int e2_top_encoder_get_pos() {
-    int pos=e2TopEncoderPos;
-    e2TopEncoderPos=0;
-    return pos;
-}
-
-int e3_top_encoder_get_pos() {
-    int pos=e3TopEncoderPos;
-    e3TopEncoderPos=0;
-    return pos;
-}
-
-int e4_top_encoder_get_pos() {
-    int pos=e4TopEncoderPos;
-    e4TopEncoderPos=0;
-    return pos;
-}
-
-int e5_top_encoder_get_pos() {
-    int pos=e5TopEncoderPos;
-    e5TopEncoderPos=0;
-    return pos;
-}
-
-int function_get_state() {
-    return function_state;
-}
-
-int band_get_state() {
-    return band_state;
-}
-
-int bandstack_get_state() {
-    return bandstack_state;
-}
-
-int mode_get_state() {
-    return mode_state;
-}
-
-int filter_get_state() {
-    return filter_state;
-}
-
-int noise_get_state() {
-    return noise_state;
-}
-
-int agc_get_state() {
-    return agc_state;
-}
-
-int mox_get_state() {
-    return mox_state;
-}
-
-int lock_get_state() {
-    return lock_state;
-}
-
-
-static int vfo_encoder_changed(void *data) {
-  if(!locked) {
-    int pos=GPOINTER_TO_INT(data);
-    vfo_step(pos);
-  }
-  //free(data);
-  return 0;
-}
-
-static void encoder_changed(int action,int pos) {
-  double value;
-  int mode;
-  int id;
-  FILTER * band_filters=filters[vfo[active_receiver->id].mode];
-  FILTER *band_filter;
-  FILTER *filter;
-  int new_val;
-
-  switch(action) {
-    case ENCODER_AF_GAIN:
-      value=active_receiver->volume;
-      value+=(double)pos/100.0;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>1.0) {
-        value=1.0;
-      }
-      set_af_gain(active_receiver->id,value);
-      break;
-    case ENCODER_AF_GAIN_RX1:
-      value=receiver[0]->volume;
-      value+=(double)pos/100.0;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>1.0) {
-        value=1.0;
-      }
-      set_af_gain(0,value);
-      break;
-    case ENCODER_AF_GAIN_RX2:
-      value=receiver[1]->volume;
-      value+=(double)pos/100.0;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>1.0) {
-        value=1.0;
-      }
-      set_af_gain(1,value);
-      break;
-    case ENCODER_RF_GAIN:
-      value=active_receiver->rf_gain;
-      value+=(double)pos;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>100.0) {
-        value=100.0;
-      }
-      set_rf_gain(active_receiver->id,value);
-      break;
-    case ENCODER_RF_GAIN_RX1:
-      value=receiver[0]->rf_gain;
-      value+=(double)pos;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>100.0) {
-        value=100.0;
-      }
-      set_rf_gain(0,value);
-      break;
-    case ENCODER_RF_GAIN_RX2:
-      value=receiver[1]->rf_gain;
-      value+=(double)pos;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>71.0) {
-        value=71.0;
-      }
-      set_rf_gain(1,value);
-      break;
-    case ENCODER_AGC_GAIN:
-      value=active_receiver->agc_gain;
-      value+=(double)pos;
-      if(value<-20.0) {
-        value=-20.0;
-      } else if(value>120.0) {
-        value=120.0;
-      }
-      set_agc_gain(active_receiver->id,value);
-      break;
-    case ENCODER_AGC_GAIN_RX1:
-      value=receiver[0]->agc_gain;
-      value+=(double)pos;
-      if(value<-20.0) {
-        value=-20.0;
-      } else if(value>120.0) {
-        value=120.0;
-      }
-      set_agc_gain(0,value);
-      break;
-    case ENCODER_AGC_GAIN_RX2:
-      value=receiver[1]->agc_gain;
-      value+=(double)pos;
-      if(value<-20.0) {
-        value=-20.0;
-      } else if(value>120.0) {
-        value=120.0;
-      }
-      set_agc_gain(1,value);
-      break;
-    case ENCODER_IF_WIDTH:
-      filter_width_changed(active_receiver->id,pos);
-      break;
-    case ENCODER_IF_WIDTH_RX1:
-      filter_width_changed(0,pos);
-      break;
-    case ENCODER_IF_WIDTH_RX2:
-      filter_width_changed(1,pos);
-      break;
-    case ENCODER_IF_SHIFT:
-      filter_shift_changed(active_receiver->id,pos);
-      break;
-    case ENCODER_IF_SHIFT_RX1:
-      filter_shift_changed(0,pos);
-      break;
-    case ENCODER_IF_SHIFT_RX2:
-      filter_shift_changed(1,pos);
-      break;
-    case ENCODER_ATTENUATION:
-      value=(double)adc_attenuation[active_receiver->adc];
-      value+=(double)pos;
-      if(have_rx_gain) {
-        if(value<-12.0) {
-          value=-12.0;
-        } else if(value>48.0) {
-          value=48.0;
-        }
-      } else {
-        if(value<0.0) {
-          value=0.0;
-        } else if (value>31.0) {
-          value=31.0;
-        }      
-      }
-      set_attenuation_value(value);
-      break;
-    case ENCODER_MIC_GAIN:
-      value=mic_gain;
-      value+=(double)pos;
-      if(value<-12.0) {
-        value=-12.0;
-      } else if(value>50.0) {
-        value=50.0;
-      }
-      set_mic_gain(value);
-      break;
-    case ENCODER_DRIVE:
-      value=getDrive();
-      value+=(double)pos;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>drive_max) {
-        value=drive_max;
-      }
-      set_drive(value);
-      break;
-    case ENCODER_RIT:
-      vfo_rit(active_receiver->id,pos);
-      break;
-    case ENCODER_RIT_RX1:
-      vfo_rit(receiver[0]->id,pos);
-      break;
-    case ENCODER_RIT_RX2:
-      vfo_rit(receiver[1]->id,pos);
-      break;
-    case ENCODER_XIT:
-      value=(double)transmitter->xit;
-      value+=(double)(pos*rit_increment);
-      if(value<-10000.0) {
-        value=-10000.0;
-      } else if(value>10000.0) {
-        value=10000.0;
-      }
-      transmitter->xit=(int)value;
-      if(protocol==NEW_PROTOCOL) {
-        schedule_high_priority();
-      }
-      g_idle_add(ext_vfo_update,NULL);
-      break;
-    case ENCODER_CW_SPEED:
-      value=(double)cw_keyer_speed;
-      value+=(double)pos;
-      if(value<1.0) {
-        value=1.0;
-      } else if(value>60.0) {
-        value=60.0;
-      }
-      cw_keyer_speed=(int)value;
-      g_idle_add(ext_vfo_update,NULL);
-      break;
-    case ENCODER_CW_FREQUENCY:
-      value=(double)cw_keyer_sidetone_frequency;
-      value+=(double)pos;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>1000.0) {
-        value=1000.0;
-      }
-      cw_keyer_sidetone_frequency=(int)value;
-      g_idle_add(ext_vfo_update,NULL);
-      break;
-    case ENCODER_PANADAPTER_HIGH:
-      value=(double)active_receiver->panadapter_high;
-      value+=(double)pos;
-      active_receiver->panadapter_high=(int)value;
-      break;
-    case ENCODER_PANADAPTER_LOW:
-      value=(double)active_receiver->panadapter_low;
-      value+=(double)pos;
-      active_receiver->panadapter_low=(int)value;
-      break;
-    case ENCODER_PANADAPTER_STEP:
-      value=(double)active_receiver->panadapter_step;
-      value+=(double)pos;
-      active_receiver->panadapter_step=(int)value;
-      break;
-    case ENCODER_WATERFALL_HIGH:
-      value=(double)active_receiver->waterfall_high;
-      value+=(double)pos;
-      active_receiver->waterfall_high=(int)value;
-      break;
-    case ENCODER_WATERFALL_LOW:
-      value=(double)active_receiver->waterfall_low;
-      value+=(double)pos;
-      active_receiver->waterfall_low=(int)value;
-      break;
-    case ENCODER_SQUELCH:
-      value=active_receiver->squelch;
-      value+=(double)pos;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>100.0) {
-        value=100.0;
-      }
-      active_receiver->squelch=value;
-      set_squelch(active_receiver);
-      break;
-    case ENCODER_SQUELCH_RX1:
-      value=receiver[0]->squelch;
-      value+=(double)pos;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>100.0) {
-        value=100.0;
-      }
-      receiver[0]->squelch=value;
-      set_squelch(receiver[0]);
-      break;
-    case ENCODER_SQUELCH_RX2:
-      value=receiver[1]->squelch;
-      value+=(double)pos;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>100.0) {
-        value=100.0;
-      }
-      receiver[1]->squelch=value;
-      set_squelch(receiver[1]);
-      break;
-    case ENCODER_COMP:
-      value=(double)transmitter->compressor_level;
-      value+=(double)pos;
-      if(value<0.0) {
-        value=0.0;
-      } else if(value>20.0) {
-        value=20.0;
-      }
-      transmitter->compressor_level=(int)value;
-      set_compression(transmitter);
-      break;
-    case ENCODER_DIVERSITY_GAIN:
-      update_diversity_gain((double)pos * 0.5);
-      break;
-    case ENCODER_DIVERSITY_GAIN_COARSE:
-      update_diversity_gain((double)pos * 2.5);
-      break;
-    case ENCODER_DIVERSITY_GAIN_FINE:
-      update_diversity_gain((double)pos * 0.1);
-      break;
-    case ENCODER_DIVERSITY_PHASE:
-      update_diversity_phase((double)pos* 0.5);
-      break;
-    case ENCODER_DIVERSITY_PHASE_COARSE:
-      update_diversity_phase((double)pos*2.5);
-      break;
-    case ENCODER_DIVERSITY_PHASE_FINE:
-      update_diversity_phase((double)pos*0.1);
-      break;
-    case ENCODER_ZOOM:
-      update_zoom((double)pos);
-      break;
-    case ENCODER_PAN:
-      update_pan((double)pos*100);
-      break;
-  }
-}
-
-static int e2_encoder_changed(void *data) {
-  int pos=GPOINTER_TO_INT(data);
-  if(active_menu==E2_MENU) {
-    encoder_select(pos);
-  } else {
-    encoder_changed(e2_encoder_action,pos);
-  }
-  return 0;
-}
-
-static int e3_encoder_changed(void *data) {
-  int pos=GPOINTER_TO_INT(data);
-  if(active_menu==E3_MENU) {
-    encoder_select(pos);
-  } else {
-    encoder_changed(e3_encoder_action,pos);
-  }
-  return 0;
-}
-
-static int e4_encoder_changed(void *data) {
-  int pos=GPOINTER_TO_INT(data);
-  if(active_menu==E4_MENU) {
-    encoder_select(pos);
-  } else {
-    encoder_changed(e4_encoder_action,pos);
-  }
-  return 0;
-}
-
-static int e5_encoder_changed(void *data) {
-  int pos=GPOINTER_TO_INT(data);
-  if(active_menu==E5_MENU) {
-    encoder_select(pos);
-  } else {
-    encoder_changed(e5_encoder_action,pos);
-  }
-  return 0;
+#endif
 }
 
-
-static int e2_top_encoder_changed(void *data) {
-  int pos=GPOINTER_TO_INT(data);
-  if(active_menu==E2_MENU) {
-    encoder_select(pos);
-  } else {
-    encoder_changed(e2_top_encoder_action,pos);
-  }
+int  gpio_left_cw_key() {
   return 0;
 }
 
-static int e3_top_encoder_changed(void *data) {
-  int pos=GPOINTER_TO_INT(data);
-  if(active_menu==E3_MENU) {
-    encoder_select(pos);
-  } else {
-    encoder_changed(e3_top_encoder_action,pos);
-  }
+int  gpio_right_cw_key() {
   return 0;
 }
 
-static int e4_top_encoder_changed(void *data) {
-  int pos=GPOINTER_TO_INT(data);
-  if(active_menu==E4_MENU) {
-    encoder_select(pos);
-  } else {
-    encoder_changed(e4_top_encoder_action,pos);
-  }
-  return 0;
-}
-
-static int e5_top_encoder_changed(void *data) {
-  int pos=GPOINTER_TO_INT(data);
-  if(active_menu==E5_MENU) {
-    encoder_select(pos);
-  } else {
-    encoder_changed(e5_top_encoder_action,pos);
-  }
-  return 0;
+int  gpio_cw_sidetone_enabled() {
+  return ENABLE_GPIO_SIDETONE;
 }
 
-static gpointer rotary_encoder_thread(gpointer data) {
-    int pos;
-
-    sleep(2);
-
-    running=1;
-    while(1) {
-
-        pos=vfo_encoder_get_pos();
-        if(pos!=0) {
-            g_idle_add(vfo_encoder_changed,GINT_TO_POINTER(pos));
-        }
-
-        pos=e2_encoder_get_pos();
-        if(pos!=0) {
-            g_idle_add(e2_encoder_changed,GINT_TO_POINTER(pos));
-        }
-
-        pos=e3_encoder_get_pos();
-        if(pos!=0) {
-            g_idle_add(e3_encoder_changed,GINT_TO_POINTER(pos));
-        }
-
-        pos=e4_encoder_get_pos();
-        if(pos!=0) {
-            g_idle_add(e4_encoder_changed,GINT_TO_POINTER(pos));
-        }
-
-        if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
-          pos=e5_encoder_get_pos();
-          if(pos!=0) {
-              g_idle_add(e5_encoder_changed,GINT_TO_POINTER(pos));
-          }
-        }
-
-         
-        if(controller==CONTROLLER2_V2) {
-          pos=e2_top_encoder_get_pos();
-          if(pos!=0) {
-              g_idle_add(e2_top_encoder_changed,GINT_TO_POINTER(pos));
-          }
-  
-          pos=e3_top_encoder_get_pos();
-          if(pos!=0) {
-              g_idle_add(e3_top_encoder_changed,GINT_TO_POINTER(pos));
-          }
-  
-          pos=e4_top_encoder_get_pos();
-          if(pos!=0) {
-              g_idle_add(e4_top_encoder_changed,GINT_TO_POINTER(pos));
-          }
-  
-          pos=e5_top_encoder_get_pos();
-          if(pos!=0) {
-            g_idle_add(e5_top_encoder_changed,GINT_TO_POINTER(pos));
-          }
-        }
-
-#ifdef sx1509
-        // buttons only generate interrupt when
-        // pushed onODER_AF_GAIN = 0,
-        function_state = 0;
-        band_state = 0;
-        bandstack_state = 0;
-        mode_state = 0;
-        filter_state = 0;
-        noise_state = 0;
-        agc_state = 0;
-        mox_state = 0;
-        lock_state = 0;
 #endif
-        if(running==0) {
-          fprintf(stderr,"gpio_thread: quitting (running==0)\n");
-          g_thread_exit(NULL);
-        }
-        usleep(100000);
-
-    }
-    return NULL;
-}
diff --git a/gpio.h b/gpio.h
index b15d8a0e15ad46ef0753de4b4f63c76665d9253e..a9072f53f409ad4411c37d2c70e66fc528ccbd44 100644 (file)
--- a/gpio.h
+++ b/gpio.h
 #ifndef _GPIO_H
 #define _GPIO_H
 
-enum {
-  NO_CONTROLLER=0,
-  CONTROLLER1,
-  CONTROLLER2_V1,
-  CONTROLLER2_V2,
-};
-
-extern int controller;
-  
-enum {
-  ENCODER_NO_ACTION=0,
-  ENCODER_AF_GAIN,
-  ENCODER_AF_GAIN_RX1,
-  ENCODER_AF_GAIN_RX2,
-  ENCODER_AGC_GAIN,
-  ENCODER_AGC_GAIN_RX1,
-  ENCODER_AGC_GAIN_RX2,
-  ENCODER_ATTENUATION,
-  ENCODER_COMP,
-  ENCODER_CW_FREQUENCY,
-  ENCODER_CW_SPEED,
-  ENCODER_DIVERSITY_GAIN,
-  ENCODER_DIVERSITY_GAIN_COARSE,
-  ENCODER_DIVERSITY_GAIN_FINE,
-  ENCODER_DIVERSITY_PHASE,
-  ENCODER_DIVERSITY_PHASE_COARSE,
-  ENCODER_DIVERSITY_PHASE_FINE,
-  ENCODER_DRIVE,
-  ENCODER_IF_SHIFT,
-  ENCODER_IF_SHIFT_RX1,
-  ENCODER_IF_SHIFT_RX2,
-  ENCODER_IF_WIDTH,
-  ENCODER_IF_WIDTH_RX1,
-  ENCODER_IF_WIDTH_RX2,
-  ENCODER_MIC_GAIN,
-  ENCODER_PAN,
-  ENCODER_PANADAPTER_HIGH,
-  ENCODER_PANADAPTER_LOW,
-  ENCODER_PANADAPTER_STEP,
-  ENCODER_RF_GAIN,
-  ENCODER_RF_GAIN_RX1,
-  ENCODER_RF_GAIN_RX2,
-  ENCODER_RIT,
-  ENCODER_RIT_RX1,
-  ENCODER_RIT_RX2,
-  ENCODER_SQUELCH,
-  ENCODER_SQUELCH_RX1,
-  ENCODER_SQUELCH_RX2,
-  ENCODER_TUNE_DRIVE,
-  ENCODER_WATERFALL_HIGH,
-  ENCODER_WATERFALL_LOW,
-  ENCODER_XIT,
-  ENCODER_ZOOM,
-  ENCODER_ACTIONS
-};
-
-extern char *encoder_string[ENCODER_ACTIONS];
-
-enum {
-  NO_ACTION=0,
-  A_TO_B,
-  A_SWAP_B,
-  AGC,
-  ANF,
-  B_TO_A,
-  BAND_MINUS,
-  BAND_PLUS,
-  BANDSTACK_MINUS,
-  BANDSTACK_PLUS,
-  CTUN,
-  DIVERSITY,
-  FILTER_MINUS,
-  FILTER_PLUS,
-  FUNCTION,
-  LOCK,
-  MENU_BAND,
-  MENU_BANDSTACK,
-  MENU_DIVERSITY,
-  MENU_FILTER,
-  MENU_FREQUENCY,
-  MENU_MEMORY,
-  MENU_MODE,
-  MENU_PS,
-  MODE_MINUS,
-  MODE_PLUS,
-  MOX,
-  MUTE,
-  NB,
-  NR,
-  PAN_MINUS,
-  PAN_PLUS,
-  PS,
-  RIT,
-  RIT_CLEAR,
-  SAT,
-  SNB,
-  SPLIT,
-  TUNE,
-  TWO_TONE,
-  XIT,
-  XIT_CLEAR,
-  ZOOM_MINUS,
-  ZOOM_PLUS,
-  SWITCH_ACTIONS
-};
-
-extern char *sw_string[SWITCH_ACTIONS];
-
-
-enum {
-  CONTROLLER1_SW1=0,
-  CONTROLLER1_SW2,
-  CONTROLLER1_SW3,
-  CONTROLLER1_SW4,
-  CONTROLLER1_SW5,
-  CONTROLLER1_SW6,
-  CONTROLLER1_SW7,
-  CONTROLLER1_SW8,
-  CONTROLLER1_SWITCHES
-};
-
-enum {
-  CONTROLLER2_SW2=0,
-  CONTROLLER2_SW3,
-  CONTROLLER2_SW4,
-  CONTROLLER2_SW5,
-  CONTROLLER2_SW6,
-  CONTROLLER2_SW7,
-  CONTROLLER2_SW8,
-  CONTROLLER2_SW9,
-  CONTROLLER2_SW10,
-  CONTROLLER2_SW11,
-  CONTROLLER2_SW12,
-  CONTROLLER2_SW13,
-  CONTROLLER2_SW14,
-  CONTROLLER2_SW15,
-  CONTROLLER2_SW16,
-  CONTROLLER2_SW17,
-  CONTROLLER2_SWITCHES
-};
+#define MAX_ENCODERS 5
+#define MAX_SWITCHES 16
+#define MAX_FUNCTIONS 6
+
+typedef struct _encoder {
+  gboolean bottom_encoder_enabled;
+  gboolean bottom_encoder_pullup;
+  gint bottom_encoder_address_a;
+  gint bottom_encoder_a_value;
+  gint bottom_encoder_address_b;
+  gint bottom_encoder_b_value;
+  gint bottom_encoder_pos;
+  gint bottom_encoder_function;
+  guchar bottom_encoder_state;
+  gint top_encoder_enabled;
+  gboolean top_encoder_pullup;
+  gint top_encoder_address_a;
+  gint top_encoder_a_value;
+  gint top_encoder_address_b;
+  gint top_encoder_b_value;
+  gint top_encoder_pos;
+  gint top_encoder_function;
+  guchar top_encoder_state;
+  gboolean switch_enabled;
+  gboolean switch_pullup;
+  gint switch_address;
+  gint switch_function;
+  gulong switch_debounce;
+} ENCODER;
+
+extern ENCODER *encoders;
+
+typedef struct _switch {
+  gboolean switch_enabled;
+  gboolean switch_pullup;
+  gint switch_address;
+  gint switch_function;
+  gulong switch_debounce;
+} SWITCH;
+
+extern SWITCH switches_no_controller[MAX_SWITCHES];
+extern SWITCH switches_controller1[MAX_FUNCTIONS][MAX_SWITCHES];
+extern SWITCH switches_controller2_v1[MAX_SWITCHES];
+extern SWITCH switches_controller2_v2[MAX_SWITCHES];
+
+extern SWITCH *switches;
 
 extern int *sw_action;
 
-extern int settle_time;
-
+extern long settle_time;
 
-extern int e2_encoder_action;
-extern int e3_encoder_action;
-extern int e4_encoder_action;
-extern int e5_encoder_action;
-
-extern int e2_top_encoder_action;
-extern int e3_top_encoder_action;
-extern int e4_top_encoder_action;
-extern int e5_top_encoder_action;
-
-extern int e2_sw_action;
-extern int e3_sw_action;
-extern int e4_sw_action;
-extern int e5_sw_action;
-
-// uses wiringpi pin numbers
-extern int ENABLE_VFO_ENCODER;
-extern int ENABLE_VFO_PULLUP;
-extern int VFO_ENCODER_A;
-extern int VFO_ENCODER_B;
-extern int ENABLE_E2_ENCODER;
-extern int ENABLE_E2_PULLUP;
-extern int E2_ENCODER_A;
-extern int E2_ENCODER_B;
-extern int E2_TOP_ENCODER_A;
-extern int E2_TOP_ENCODER_B;
-extern int E2_FUNCTION;
-extern int ENABLE_E3_ENCODER;
-extern int ENABLE_E3_PULLUP;
-extern int E3_ENCODER_A;
-extern int E3_ENCODER_B;
-extern int E3_TOP_ENCODER_A;
-extern int E3_TOP_ENCODER_B;
-extern int E3_FUNCTION;
-extern int ENABLE_E4_ENCODER;
-extern int ENABLE_E4_PULLUP;
-extern int E4_ENCODER_A;
-extern int E4_ENCODER_B;
-extern int E4_TOP_ENCODER_A;
-extern int E4_TOP_ENCODER_B;
-extern int E4_FUNCTION;
-extern int ENABLE_E5_ENCODER;
-extern int ENABLE_E5_PULLUP;
-extern int E5_ENCODER_A;
-extern int E5_ENCODER_B;
-extern int E5_TOP_ENCODER_A;
-extern int E5_TOP_ENCODER_B;
-extern int E5_FUNCTION;
-
-extern int ENABLE_S1_BUTTON;
-extern int S1_BUTTON;
-extern int ENABLE_S2_BUTTON;
-extern int S2_BUTTON;
-extern int ENABLE_S3_BUTTON;
-extern int S3_BUTTON;
-extern int ENABLE_S4_BUTTON;
-extern int S4_BUTTON;
-extern int ENABLE_S5_BUTTON;
-extern int S5_BUTTON;
-extern int ENABLE_S6_BUTTON;
-extern int S6_BUTTON;
+extern int process_function_switch(void *data);
+extern void gpio_set_defaults(int ctrlr);
+extern void gpio_restore_actions();
+extern void gpio_restore_state();
+extern void gpio_save_state();
+extern void gpio_save_actions();
+extern int gpio_init();
+extern void gpio_close();
 
-extern int ENABLE_MOX_BUTTON;
-extern int MOX_BUTTON;
-extern int ENABLE_FUNCTION_BUTTON;
-extern int FUNCTION_BUTTON;
 #ifdef LOCALCW
 extern int CWL_BUTTON;
 extern int CWR_BUTTON;
@@ -245,29 +93,4 @@ extern int  gpio_right_cw_key();
 extern int  gpio_cw_sidetone_enabled();
 #endif
 
-#ifdef PTT
-extern int ENABLE_PTT_GPIO;
-extern int PTT_GPIO;
-extern int PTT_ACTIVE_LOW;
-#endif
-
-extern void gpio_set_defaults(int ctrlr);
-extern void gpio_restore_actions();
-extern void gpio_restore_state();
-extern void gpio_save_state();
-extern void gpio_save_actions();
-extern int gpio_init();
-extern void gpio_close();
-extern int vfo_encoder_get_pos();
-extern int af_encoder_get_pos();
-extern int af_function_get_state();
-extern int rf_encoder_get_pos();
-extern int rf_function_get_state();
-extern int function_get_state();
-extern int band_get_state();
-extern int mode_get_state();
-extern int filter_get_state();
-extern int noise_get_state();
-extern int mox_get_state();
-
 #endif
diff --git a/i2c.c b/i2c.c
index 7798b8e61264531657f67f676e067b41fb7398b1..78fc43b9e9db29b6945bfc0cc66de563693dced2 100644 (file)
--- a/i2c.c
+++ b/i2c.c
@@ -1,16 +1,17 @@
+#ifdef GPIO
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <linux/i2c-dev.h>
-//#include <i2c/smbus.h>
+#include <i2c/smbus.h>
 #include <sys/ioctl.h>
 #include <fcntl.h>
 
 #include <gtk/gtk.h>
-//#include "i2c.h"
-#include "wiringPiI2C.h"
+#include "i2c.h"
+#include "actions.h"
 #include "gpio.h"
 #include "band.h"
 #include "band_menu.h"
@@ -44,31 +45,31 @@ static int fd;
 #define SW_17 0X0100
 
 unsigned int i2c_sw[16]=
-    { SW_2,SW_3,SW_4,SW_5,SW_6,SW_7,SW_8,SW_9,
-      SW_10,SW_11,SW_12,SW_13,SW_14,SW_15,SW_16,SW_17 };
+    { SW_2,SW_3,SW_4,SW_5,SW_6,SW_14,SW_15,SW_13,
+      SW_12,SW_11,SW_10,SW_9,SW_7,SW_8,SW_16,SW_17 };
 
-static int write_byte_data(unsigned char addr,unsigned char reg, unsigned char data) {
+static int write_byte_data(unsigned char reg, unsigned char data) {
   int rc;
 
-  rc=wiringPiI2CWriteReg8(fd,reg,data);
+  if(i2c_smbus_write_byte_data(fd,reg,data&0xFF)<0) {
+    g_print("%s: write REG_GCONF config failed: addr=%02X %s\n",__FUNCTION__,i2c_address_1,g_strerror(errno));
+  }
   
-  return 0;
+  return rc;
 }
 
-static unsigned char read_byte_data(unsigned char addr,unsigned char reg) {
-  int rc;
-
-  rc=wiringPiI2CReadReg8(fd,reg);
+static unsigned char read_byte_data(unsigned char reg) {
+  __s32 data;
 
-  return rc;
+  data=i2c_smbus_read_byte_data(fd,reg);
+  return data&0xFF;
 }
 
-static unsigned int read_word_data(unsigned char addr,unsigned char reg) {
-  int rc;
-
-  rc=wiringPiI2CReadReg16(fd,reg);
+static unsigned int read_word_data(unsigned char reg) {
+  __s32 data;
 
-  return rc;
+  data=i2c_smbus_read_word_data(fd,reg);
+  return data&0xFFFF;
 }
 
 
@@ -81,157 +82,21 @@ void i2c_interrupt() {
   unsigned int ints;
 
   do {
-    flags=read_word_data(i2c_address_1,0x0E);
+    flags=read_word_data(0x0E);
     if(flags) {
-      ints=read_word_data(i2c_address_1,0x10);
-//g_print("i2c_interrupt: flags=%04X ints=%04X\n",flags,ints);
+      ints=read_word_data(0x10);
+g_print("%s: flags=%04X ints=%04X\n",__FUNCTION__,flags,ints);
       if(ints) {
         int i;
         for(i=0;i<16;i++) {
           if(i2c_sw[i]==ints) break;
         }
         if(i<16) {
-//g_print("i1c_interrupt: sw=%d action=%d\n",i,sw_action[i]);
-          switch(sw_action[i]) {
-            case TUNE:
-              if(can_transmit) {
-                int tune=getTune();
-                if(tune==0) tune=1; else tune=0;
-                  g_idle_add(ext_tune_update,GINT_TO_POINTER(tune));
-              }
-              break;
-            case MOX:
-              if(can_transmit) {
-                int mox=getMox();
-                if(mox==0) mox=1; else mox=0;
-                g_idle_add(ext_mox_update,GINT_TO_POINTER(mox));
-              }
-              break;
-            case PS:
-#ifdef PURESIGNAL
-              if(can_transmit) g_idle_add(ext_ps_update,NULL);
-#endif
-              break;
-            case TWO_TONE:
-              if(can_transmit) g_idle_add(ext_two_tone,NULL);
-              break;
-            case NR:
-              g_idle_add(ext_nr_update,NULL);
-              break;
-            case NB:
-              g_idle_add(ext_nb_update,NULL);
-              break;
-            case SNB:
-              g_idle_add(ext_snb_update,NULL);
-              break;
-            case RIT:
-              g_idle_add(ext_rit_update,NULL);
-              break;
-            case RIT_CLEAR:
-              g_idle_add(ext_rit_clear,NULL);
-              break;
-            case XIT:
-              if(can_transmit) g_idle_add(ext_xit_update,NULL);
-              break;
-            case XIT_CLEAR:
-              if(can_transmit) g_idle_add(ext_xit_clear,NULL);
-              break;
-            case BAND_PLUS:
-              g_idle_add(ext_band_plus,NULL);
-              break;
-            case BAND_MINUS:
-              g_idle_add(ext_band_minus,NULL);
-              break;
-            case BANDSTACK_PLUS:
-              g_idle_add(ext_bandstack_plus,NULL);
-              break;
-            case BANDSTACK_MINUS:
-              g_idle_add(ext_bandstack_minus,NULL);
-              break;
-            case MODE_PLUS:
-              g_idle_add(ext_mode_plus,NULL);
-              break;
-            case MODE_MINUS:
-              g_idle_add(ext_mode_minus,NULL);
-              break;
-            case FILTER_PLUS:
-              g_idle_add(ext_filter_plus,NULL);
-              break;
-            case FILTER_MINUS:
-              g_idle_add(ext_filter_minus,NULL);
-              break;
-            case A_TO_B:
-              g_idle_add(ext_vfo_a_to_b,NULL);
-              break;
-            case B_TO_A:
-              g_idle_add(ext_vfo_b_to_a,NULL);
-              break;
-            case A_SWAP_B:
-              g_idle_add(ext_vfo_a_swap_b,NULL);
-              break;
-            case LOCK:
-              g_idle_add(ext_lock_update,NULL);
-              break;
-            case CTUN:
-              g_idle_add(ext_ctun_update,NULL);
-              break;
-            case AGC:
-              g_idle_add(ext_agc_update,NULL);
-              break;
-            case SPLIT:
-              if(can_transmit) g_idle_add(ext_split_toggle,NULL);
-              break;
-            case DIVERSITY:
-              g_idle_add(ext_diversity_update,GINT_TO_POINTER(0));
-              break;
-            case SAT:
-              if(can_transmit) g_idle_add(ext_sat_update,NULL);
-              break;
-            case MENU_BAND:
-              g_idle_add(ext_band_update,NULL);
-              break;
-            case MENU_BANDSTACK:
-              g_idle_add(ext_bandstack_update,NULL);
-              break;
-            case MENU_MODE:
-              g_idle_add(ext_mode_update,NULL);
-              break;
-            case MENU_FILTER:
-              g_idle_add(ext_filter_update,NULL);
-              break;
-            case MENU_FREQUENCY:
-              g_idle_add(ext_frequency_update,NULL);
-              break;
-            case MENU_MEMORY:
-              g_idle_add(ext_memory_update,NULL);
-              break;
-            case MENU_DIVERSITY:
-              g_idle_add(ext_diversity_update,GINT_TO_POINTER(1));
-              break;
-            case MENU_PS:
-#ifdef PURESIGNAL
-              g_idle_add(ext_start_ps,NULL);
-#endif
-              break;
-            case FUNCTION:
-              g_idle_add(ext_function_update,NULL);
-              break;
-            case MUTE:
-              g_idle_add(ext_mute_update,NULL);
-              break;
-            case PAN_MINUS:
-              g_idle_add(ext_pan_update,GINT_TO_POINTER(-100));
-              break;
-            case PAN_PLUS:
-              g_idle_add(ext_pan_update,GINT_TO_POINTER(100));
-              break;
-            case ZOOM_MINUS:
-              g_idle_add(ext_zoom_update,GINT_TO_POINTER(-1));
-              break;
-            case ZOOM_PLUS:
-              g_idle_add(ext_zoom_update,GINT_TO_POINTER(1));
-              break;
-          }
+g_print("%s: switches=%p sw=%d action=%d\n",__FUNCTION__,switches,i,switches[i].switch_function);
+          SWITCH_ACTION *a=g_new(SWITCH_ACTION,1);
+          a->action=switches[i].switch_function;
+          a->state=PRESSED;
+          g_idle_add(switch_action,a);
         }
       }
     }
@@ -242,56 +107,61 @@ void i2c_init() {
 
   int flags, ints;
 
-fprintf(stderr,"i2c_init: %s\n",i2c_device);
-
-  fd=wiringPiI2CSetupInterface(i2c_device, i2c_address_1);
+  g_print("%s: open i2c device %s\n",__FUNCTION__,i2c_device);
+  fd=open(i2c_device, O_RDWR);
   if(fd<0) {
-    g_print("i2c_init failed: fd=%d\n",fd);
+    g_print("%s: open i2c device %s failed: %s\n",__FUNCTION__,i2c_device,g_strerror(errno));
+    return;
+  }
+  g_print("%s: open i2c device %s fd=%d\n",__FUNCTION__,i2c_device,fd);
+
+  if (ioctl(fd, I2C_SLAVE, i2c_address_1) < 0) {
+    g_print("%s: ioctl i2c slave %d failed: %s\n",__FUNCTION__,i2c_address_1,g_strerror(errno));
     return;
   }
 
   // setup i2c
-  if(write_byte_data(i2c_address_1,0x0A,0x44)<0) return;
-  if(write_byte_data(i2c_address_1,0x0B,0x44)<0) return;
+  if(write_byte_data(0x0A,0x44)<0) return;
+  if(write_byte_data(0x0B,0x44)<0) return;
 
   // disable interrupt
-  if(write_byte_data(i2c_address_1,0x04,0x00)<0) return;
-  if(write_byte_data(i2c_address_1,0x05,0x00)<0) return;
+  if(write_byte_data(0x04,0x00)<0) return;
+  if(write_byte_data(0x05,0x00)<0) return;
 
   // clear defaults
-  if(write_byte_data(i2c_address_1,0x06,0x00)<0) return;
-  if(write_byte_data(i2c_address_1,0x07,0x00)<0) return;
+  if(write_byte_data(0x06,0x00)<0) return;
+  if(write_byte_data(0x07,0x00)<0) return;
 
   // OLAT
-  if(write_byte_data(i2c_address_1,0x14,0x00)<0) return;
-  if(write_byte_data(i2c_address_1,0x15,0x00)<0) return;
+  if(write_byte_data(0x14,0x00)<0) return;
+  if(write_byte_data(0x15,0x00)<0) return;
 
   // set GPIOA for pullups
-  if(write_byte_data(i2c_address_1,0x0C,0xFF)<0) return;
-  if(write_byte_data(i2c_address_1,0x0D,0xFF)<0) return;
+  if(write_byte_data(0x0C,0xFF)<0) return;
+  if(write_byte_data(0x0D,0xFF)<0) return;
 
   // reverse polarity
-  if(write_byte_data(i2c_address_1,0x02,0xFF)<0) return;
-  if(write_byte_data(i2c_address_1,0x03,0xFF)<0) return;
+  if(write_byte_data(0x02,0xFF)<0) return;
+  if(write_byte_data(0x03,0xFF)<0) return;
 
   // set GPIOA/B for input
-  if(write_byte_data(i2c_address_1,0x00,0xFF)<0) return;
-  if(write_byte_data(i2c_address_1,0x01,0xFF)<0) return;
+  if(write_byte_data(0x00,0xFF)<0) return;
+  if(write_byte_data(0x01,0xFF)<0) return;
 
   // INTCON
-  if(write_byte_data(i2c_address_1,0x08,0x00)<0) return;
-  if(write_byte_data(i2c_address_1,0x09,0x00)<0) return;
+  if(write_byte_data(0x08,0x00)<0) return;
+  if(write_byte_data(0x09,0x00)<0) return;
 
   // setup for an MCP23017 interrupt
-  if(write_byte_data(i2c_address_1,0x04,0xFF)<0) return;
-  if(write_byte_data(i2c_address_1,0x05,0xFF)<0) return;
+  if(write_byte_data(0x04,0xFF)<0) return;
+  if(write_byte_data(0x05,0xFF)<0) return;
 
   // flush any interrupts
   int count=0;
   do {
-    flags=read_word_data(i2c_address_1,0x0E);
+    flags=read_word_data(0x0E);
     if(flags) {
-      ints=read_word_data(i2c_address_1,0x10);
+      ints=read_word_data(0x10);
       fprintf(stderr,"flush interrupt: flags=%04X ints=%04X\n",flags,ints);
       count++;
       if(count==10) {
@@ -301,3 +171,4 @@ fprintf(stderr,"i2c_init: %s\n",i2c_device);
   } while(flags!=0);
   
 }
+#endif
index dadc20adc07661dd6205558dc37aba751a965615..c81e7cfc8c1174a85112532f4470cf536220d810 100644 (file)
--- a/iambic.c
+++ b/iambic.c
  **************************************************************************************************************
  */
 
+#include <gtk/gtk.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <sys/mman.h>
 
-#ifdef GPIO
+#ifdef LOCALCW
 #include "gpio.h"
 #endif
 #include "radio.h"
@@ -240,14 +241,6 @@ extern int clock_nanosleep(clockid_t __clock_id, int __flags,
       struct timespec *__rem);
 #endif
 
-#ifndef GPIO
-//
-// Dummy functions if compiled without GPIO
-//
-int gpio_cw_sidetone_enabled() { return 0; }
-void gpio_cw_sidetone_set(int level) {}
-#endif
-
 static void keyer_straight_key(int state) {
   //
   // Interface for simple key-down action e.g. from a MIDI message
index 60c7b01d6222c399eb69115b95b9f766b7b24279..5336e66ea9610b5a87648ac6241fbbc57955c7ee 100644 (file)
  *
  */
 
+#include <gtk/gtk.h>
+#include "discovered.h"
+#include "receiver.h"
+#include "transmitter.h"
+#include "receiver.h"
+#include "adc.h"
+#include "dac.h"
+#include "radio.h"
 #include "midi.h"
+#include "midi_menu.h"
+#include "alsa_midi.h"
 
 #ifdef __APPLE__
 
 #include <CoreAudio/HostTime.h>
 #include <CoreAudio/CoreAudio.h>
 
+MIDI_DEVICE midi_devices[MAX_MIDI_DEVICES];
+int n_midi_devices;
+int running;
+
 //
 // MIDI callback function
 // called by MacOSX when data from the specified MIDI device arrives.
@@ -69,6 +83,8 @@ static enum {
         CMD_PITCH,
 } command;
 
+static gboolean configure=FALSE;
+
 static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) {
     int i,j,byte,chan,arg1,arg2;
     MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
@@ -119,19 +135,39 @@ static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *co
                            // messages with velocity == 0 when releasing
                            // a push-button.
                            if (arg2 == 0) {
-                             NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                             if(configure) {
+                               NewMidiConfigureEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                             } else {
+                               NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                             }
                            } else {
-                             NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 1);
+                             if(configure) {
+                               NewMidiConfigureEvent(MIDI_EVENT_NOTE, chan, arg1, 1);
+                             } else {
+                               NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 1);
+                             }
                            }
                            break;
                         case CMD_NOTEOFF:
-                           NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                           if(configure) {
+                             NewMidiConfigureEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                           } else {
+                             NewMidiEvent(MIDI_EVENT_NOTE, chan, arg1, 0);
+                           }
                            break;
                         case CMD_CTRL:
-                           NewMidiEvent(MIDI_EVENT_CTRL, chan, arg1, arg2);
+                           if(configure) {
+                             NewMidiConfigureEvent(MIDI_EVENT_CTRL, chan, arg1, arg2);
+                           } else {
+                             NewMidiEvent(MIDI_EVENT_CTRL, chan, arg1, arg2);
+                           }
                            break;
                         case CMD_PITCH:
-                           NewMidiEvent(MIDI_EVENT_PITCH, chan, 0, arg1+128*arg2);
+                           if(configure) {
+                             NewMidiConfigureEvent(MIDI_EVENT_PITCH, chan, 0, arg1+128*arg2);
+                           } else {
+                             NewMidiEvent(MIDI_EVENT_PITCH, chan, 0, arg1+128*arg2);
+                           }
                            break;
                     }
                     state=STATE_SKIP;
@@ -142,54 +178,159 @@ static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *co
     } // j-loop through the list of packets
 }
 
+//
+// store the ports and clients locally such that we
+// can properly close a MIDI connection.
+// This can be local static data, no one outside this file
+// needs it.
+//
+static MIDIPortRef myMIDIports[MAX_MIDI_DEVICES];
+static MIDIClientRef myClients[MAX_MIDI_DEVICES];
 
-void register_midi_device(char *myname) {
-    unsigned long nDevices;
-    int i;
-    CFStringRef pname;
-    char name[100];
-    int FoundMIDIref=-1;
-    int mylen=strlen(myname);
+void close_midi_device(index) {
+    fprintf(stderr,"%s index=%d\n",__FUNCTION__, index);
+    if (index < 0 || index > n_midi_devices) return;
+    //
+    // This should release the resources associated with the pending connection
+    //
+    MIDIPortDisconnectSource(myMIDIports[index], MIDIGetSource(index));
+    midi_devices[index].active=0;
+}
 
+void register_midi_device(int index) {
+    OSStatus osret;
 
+    g_print("%s: index=%d\n",__FUNCTION__,index);
 //
-// Go through the list of MIDI devices and
-// look whether the one we are looking for is there
+//  Register a callback routine for the device
 //
+    if (index < 0 || index > MAX_MIDI_DEVICES) return;
+
+     myClients[index]=0;
+     myMIDIports[index] = 0;
+     //Create client and port, and connect
+     osret=MIDIClientCreate(CFSTR("piHPSDR"),NULL,NULL, &myClients[index]);
+     if (osret !=0) {
+       g_print("%s: MIDIClientCreate failed with ret=%d\n", __FUNCTION__, (int) osret);
+       return;
+     }
+     osret=MIDIInputPortCreate(myClients[index], CFSTR("FromMIDI"), ReadMIDIdevice, NULL, &myMIDIports[index]);
+     if (osret !=0) {
+        g_print("%s: MIDIInputPortCreate failed with ret=%d\n", __FUNCTION__, (int) osret);
+        return;
+     }
+     osret=MIDIPortConnectSource(myMIDIports[index] ,MIDIGetSource(index), NULL);
+     if (osret != 0) {
+        g_print("%s: MIDIPortConnectSource failed with ret=%d\n", __FUNCTION__, (int) osret);
+        return;
+     }
+     //
+     // Now we have successfully opened the device.
+     //
+     midi_devices[index].active=1;
+     return;
+}
+
+void get_midi_devices() {
+    int n;
+    int i;
+    CFStringRef pname;   // MacOS name of the device
+    char name[100];      // C name of the device
+    OSStatus osret;
+    static int first=1;
+
+    if (first) {
+      //
+      // perhaps not necessary in C, but good programming practise:
+      // initialize the table upon the first call
+      //
+      first=0;
+      for (i=0; i<MAX_MIDI_DEVICES; i++) {
+        midi_devices[i].name=NULL;
+        midi_devices[i].active=0;
+      }
+    }
 
-    nDevices=MIDIGetNumberOfSources();
-    for (i=0; i<nDevices; i++) {
-       MIDIEndpointRef dev = MIDIGetSource(i);
-       if (dev != 0) {
-           MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &pname);
-           CFStringGetCString(pname, name, sizeof(name), 0);
-           CFRelease(pname);
+//
+//  This is called at startup (via midi_restore) and each time
+//  the MIDI menu is opened. So we have to take care that this
+//  function is essentially a no-op if the device list has not
+//  changed.
+//  If the device list has changed because of hot-plugging etc.
+//  close any MIDI device which changed position and mark
+//  it as inactive. Note that upon a hot-plug, MIDI devices that were
+//  there before may change its position in the device list and will then
+//  be closed.
+//
+    n=MIDIGetNumberOfSources();
+    n_midi_devices=0;
+    for (i=0; i<n; i++) {
+        MIDIEndpointRef dev = MIDIGetSource(i);
+        if (dev != 0) {
+            osret=MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &pname);
+            if (osret !=0) break; // in this case pname is invalid
+            CFStringGetCString(pname, name, sizeof(name), 0);
+            CFRelease(pname);
             //
             // Some users have reported that MacOS reports a string of length zero
             // for some MIDI devices. In this case, we replace the name by
-            // "NoPort"
+            // "NoPort<n>"
             //
-            if (strlen(name) == 0) strcpy(name,"NoPort");
-           if (!strncmp(name, myname, mylen)) {
-               FoundMIDIref=i;
-               fprintf(stderr,"MIDI: registering device >%s<\n", name);
-           } else {
-               fprintf(stderr,"MIDI: looking for >%s< so >%s< does not match\n", myname,name);
-           }
-       }
+            if (strlen(name) == 0) sprintf(name,"NoPort%d",n_midi_devices);
+            g_print("%s: %s\n",__FUNCTION__,name);
+            if (midi_devices[n_midi_devices].name != NULL) {
+              if (strncmp(name, midi_devices[n_midi_devices].name,sizeof(name))) {
+                //
+                // This slot was occupied and the names do not match:
+                // Close device (if active), insert new name
+                //
+                if (midi_devices[n_midi_devices].active) {
+                  close_midi_device(n_midi_devices);
+                }
+                g_free(midi_devices[n_midi_devices].name);
+                midi_devices[n_midi_devices].name=g_new(gchar,strlen(name)+1);
+                strcpy(midi_devices[n_midi_devices].name, name);
+              } else {
+                //
+                // This slot was occupied and the names match: do nothing!
+                // If there was no hot-plug or hot-unplug, we should always
+                // arrive here!
+                //
+              }
+            } else {
+             //
+              // This slot was unoccupied. Insert name and mark inactive
+              //
+              midi_devices[n_midi_devices].name=g_new(gchar,strlen(name)+1);
+              strcpy(midi_devices[n_midi_devices].name, name);
+              midi_devices[n_midi_devices].active=0;
+            }
+            n_midi_devices++;
+        }
+        //
+        // If there are more devices than we have slots in our Table
+        // just stop processing.
+        //
+        if (n_midi_devices >= MAX_MIDI_DEVICES) break;
     }
-
-//
-// If we found "our" device, register a callback routine
-//
-
-    if (FoundMIDIref >= 0) {
-        MIDIClientRef client = 0;
-        MIDIPortRef myMIDIport = 0;
-        //Create client
-        MIDIClientCreate(CFSTR("piHPSDR"),NULL,NULL, &client);
-        MIDIInputPortCreate(client, CFSTR("FromMIDI"), ReadMIDIdevice, NULL, &myMIDIport);
-        MIDIPortConnectSource(myMIDIport,MIDIGetSource(FoundMIDIref), NULL);
+    g_print("%s: number of devices=%d\n",__FUNCTION__,n_midi_devices);
+    //
+    // Get rid of all devices lingering around above the high-water mark
+    // (this happens in the case of hot-unplugging)
+    //
+    for (i=n_midi_devices; i<MAX_MIDI_DEVICES; i++) {
+      if (midi_devices[i].active) {
+        close_midi_device(i);
+      }
+      if (midi_devices[i].name != NULL) {
+        g_free(midi_devices[i].name);
+        midi_devices[i].name=NULL;
+      }
     }
 }
+
+void configure_midi_device(gboolean state) {
+  configure=state;
+}
+
 #endif
diff --git a/main.h b/main.h
index 44b7eff3cf53718feb4177ddde2253f1dc86fa7c..bf1e33fda8d82bf17df14b88d21cfb2b62270e59 100644 (file)
--- a/main.h
+++ b/main.h
 #include <sys/utsname.h>
 extern struct utsname unameData;
 
+enum {
+  NO_CONTROLLER,
+  CONTROLLER1,
+  CONTROLLER2_V1,
+  CONTROLLER2_V2
+};
+
+extern gint controller;
+
 extern gint display_width;
 extern gint display_height;
 extern gint full_screen;
diff --git a/midi.h b/midi.h
index 54b14f42a736b211fe636cc8c2f8dec75f35d5a6..2a290139c851ae03130e9be5df3fdbd476920be5 100644 (file)
--- a/midi.h
+++ b/midi.h
@@ -56,17 +56,39 @@ enum MIDIaction {
   MIDI_ACTION_ANF,             // ANF:                 toggel ANF on/off
   MIDI_ACTION_ATT,             // ATT:                 Step attenuator or Programmable attenuator
   MIDI_ACTION_VFO_B2A,         // B2A:                 VFO B -> A
+  MIDI_ACTION_BAND_10,          // BAND10
+  MIDI_ACTION_BAND_12,          // BAND12
+  MIDI_ACTION_BAND_1240,        // BAND1240
+  MIDI_ACTION_BAND_144,         // BAND144
+  MIDI_ACTION_BAND_15,          // BAND15
+  MIDI_ACTION_BAND_160,         // BAND160
+  MIDI_ACTION_BAND_17,          // BAND17
+  MIDI_ACTION_BAND_20,          // BAND20
+  MIDI_ACTION_BAND_220,         // BAND220
+  MIDI_ACTION_BAND_2300,        // BAND2300
+  MIDI_ACTION_BAND_30,          // BAND30
+  MIDI_ACTION_BAND_3400,        // BAND3400
+  MIDI_ACTION_BAND_40,          // BAND40
+  MIDI_ACTION_BAND_430,         // BAND430
+  MIDI_ACTION_BAND_6,           // BAND6
+  MIDI_ACTION_BAND_60,          // BAND60
+  MIDI_ACTION_BAND_70,          // BAND70
+  MIDI_ACTION_BAND_80,          // BAND80
+  MIDI_ACTION_BAND_902,         // BAND902
+  MIDI_ACTION_BAND_AIR,         // BANDAIR
   MIDI_ACTION_BAND_DOWN,       // BANDDOWN:            cycle through bands downwards
+  MIDI_ACTION_BAND_GEN,                // BANDGEN
   MIDI_ACTION_BAND_UP,         // BANDUP:              cycle through bands upwards
+  MIDI_ACTION_BAND_WWV,                // BANDWWVUP:           cycle through bands upwards
   MIDI_ACTION_COMPRESS,                // COMPRESS:            TX compressor value
   MIDI_ACTION_CTUN,            // CTUN:                toggle CTUN on/off
   MIDI_ACTION_VFO,             // CURRVFO:             change VFO frequency
-  MIDI_ACTION_CWKEY,           // CWKEY:               Unconditional CW key-down/up (outside keyer)
-  MIDI_ACTION_CWL,             // CWL:                 Left paddle pressed (use with ONOFF)
-  MIDI_ACTION_CWR,             // CWR:                 Right paddle pressed (use with ONOFF)
+  MIDI_ACTION_CWKEYER,         // CW(Keyer):           Unconditional CW key-down/up (outside keyer)
+  MIDI_ACTION_CWLEFT,          // CWLEFT:              Left paddle pressed (use with ONOFF)
+  MIDI_ACTION_CWRIGHT,         // CWRIGHT:             Right paddle pressed (use with ONOFF)
   MIDI_ACTION_CWSPEED,         // CWSPEED:             Set speed of (iambic) CW keyer
   MIDI_ACTION_DIV_COARSEGAIN,  // DIVCOARSEGAIN:       change DIVERSITY gain in large increments
-  MIDI_ACTION_DIV_COARSEPHASE, // DIVPHASE:            change DIVERSITY phase in large increments
+  MIDI_ACTION_DIV_COARSEPHASE, // DIVCOARSEPHASE:      change DIVERSITY phase in large increments
   MIDI_ACTION_DIV_FINEGAIN,    // DIVFINEGAIN:         change DIVERSITY gain in small increments
   MIDI_ACTION_DIV_FINEPHASE,   // DIVFINEPHASE:        change DIVERSITY phase in small increments
   MIDI_ACTION_DIV_GAIN,                // DIVGAIN:             change DIVERSITY gain in medium increments
@@ -76,6 +98,13 @@ enum MIDIaction {
   MIDI_ACTION_FILTER_DOWN,     // FILTERDOWN:          cycle through filters downwards
   MIDI_ACTION_FILTER_UP,       // FILTERUP:            cycle through filters upwards
   MIDI_ACTION_LOCK,            // LOCK:                lock VFOs, disable frequency changes
+  MIDI_ACTION_MEM_RECALL_M0,    // RECALLM0:           load current freq/mode/filter from memory slot #0
+  MIDI_ACTION_MEM_RECALL_M1,    // RECALLM1:           load current freq/mode/filter from memory slot #1
+  MIDI_ACTION_MEM_RECALL_M2,    // RECALLM2:           load current freq/mode/filter from memory slot #2
+  MIDI_ACTION_MEM_RECALL_M3,    // RECALLM3:           load current freq/mode/filter from memory slot #3
+  MIDI_ACTION_MEM_RECALL_M4,    // RECALLM4:           load current freq/mode/filter from memory slot #4
+  MIDI_ACTION_MENU_FILTER,      // MENU_FILTER
+  MIDI_ACTION_MENU_MODE,        // MENU_MODE
   MIDI_ACTION_MIC_VOLUME,      // MICGAIN:             MIC gain
   MIDI_ACTION_MODE_DOWN,       // MODEDOWN:            cycle through modes downwards
   MIDI_ACTION_MODE_UP,         // MODEUP:              cycle through modes upwards
@@ -83,17 +112,24 @@ enum MIDIaction {
   MIDI_ACTION_MUTE,            // MUTE:                toggle mute on/off
   MIDI_ACTION_NB,              // NOISEBLANKER:        cycle through NoiseBlanker states (none, NB, NB2)
   MIDI_ACTION_NR,              // NOISEREDUCTION:      cycle through NoiseReduction states (none, NR, NR2)
+  MIDI_ACTION_NUMPAD_0,         // NUMPAD0
+  MIDI_ACTION_NUMPAD_1,         // NUMPAD1
+  MIDI_ACTION_NUMPAD_2,         // NUMPAD2
+  MIDI_ACTION_NUMPAD_3,         // NUMPAD3
+  MIDI_ACTION_NUMPAD_4,         // NUMPAD4
+  MIDI_ACTION_NUMPAD_5,         // NUMPAD5
+  MIDI_ACTION_NUMPAD_6,         // NUMPAD6
+  MIDI_ACTION_NUMPAD_7,         // NUMPAD7
+  MIDI_ACTION_NUMPAD_8,         // NUMPAD8
+  MIDI_ACTION_NUMPAD_9,         // NUMPAD9
+  MIDI_ACTION_NUMPAD_CL,        // NUMPADCL
+  MIDI_ACTION_NUMPAD_ENTER,     // NUMPADENTER
   MIDI_ACTION_PAN,             // PAN:                 change panning of panadater/waterfall when zoomed
   MIDI_ACTION_PAN_HIGH,                // PANHIGH:             "high" value of current panadapter
   MIDI_ACTION_PAN_LOW,         // PANLOW:              "low" value of current panadapter
   MIDI_ACTION_PRE,             // PREAMP:              preamp on/off
-  MIDI_ACTION_PTTONOFF,                // PTT:                 set PTT state to "on" or "off"
+  MIDI_ACTION_PTTKEYER,                // PTT(Keyer):                  set PTT state to "on" or "off"
   MIDI_ACTION_PS,              // PURESIGNAL:          toggle PURESIGNAL on/off
-  MIDI_ACTION_MEM_RECALL_M0,    // RECALLM0:           load current freq/mode/filter from memory slot #0
-  MIDI_ACTION_MEM_RECALL_M1,    // RECALLM1:           load current freq/mode/filter from memory slot #1
-  MIDI_ACTION_MEM_RECALL_M2,    // RECALLM2:           load current freq/mode/filter from memory slot #2
-  MIDI_ACTION_MEM_RECALL_M3,    // RECALLM3:           load current freq/mode/filter from memory slot #3
-  MIDI_ACTION_MEM_RECALL_M4,    // RECALLM4:           load current freq/mode/filter from memory slot #4
   MIDI_ACTION_RF_GAIN,         // RFGAIN:              receiver RF gain
   MIDI_ACTION_TX_DRIVE,                // RFPOWER:             adjust TX RF output power
   MIDI_ACTION_RIT_CLEAR,       // RITCLEAR:            clear RIT and XIT value
@@ -122,6 +158,7 @@ enum MIDIaction {
   MIDI_ACTION_ZOOM,            // ZOOM:                change zoom factor
   MIDI_ACTION_ZOOM_UP,         // ZOOMUP:              change zoom factor
   MIDI_ACTION_ZOOM_DOWN,       // ZOOMDOWN:            change zoom factor
+  MIDI_ACTION_LAST,             // flag for end of list
 };
 
 //
@@ -148,12 +185,15 @@ enum MIDIaction {
 //
 
 enum MIDItype {
- MIDI_TYPE_NONE=0,
- MIDI_TYPE_KEY,          // Button (press event)
- MIDI_TYPE_KNOB,         // Knob   (value between 0 and 100)
- MIDI_TYPE_WHEEL         // Wheel  (direction and speed)
+ MIDI_TYPE_NONE =0,
+ MIDI_TYPE_KEY  =1,      // Button (press event)
+ MIDI_TYPE_KNOB =2,      // Knob   (value between 0 and 100)
+ MIDI_TYPE_WHEEL=4       // Wheel  (direction and speed)
 };
 
+extern gchar *midi_types[];
+extern gchar *midi_events[];
+
 //
 // MIDIevent encodes the actual MIDI event "seen" in Layer-1 and
 // passed to Layer-2. MIDI_NOTE events end up as MIDI_KEY and
@@ -167,6 +207,15 @@ enum MIDIevent {
  MIDI_EVENT_PITCH
 };
 
+typedef struct _action_table {
+  enum MIDIaction action;
+  const char *str;
+  enum MIDItype type;
+  int onoff;
+} ACTION_TABLE;
+
+extern ACTION_TABLE ActionTable[];
+
 //
 // Data structure for Layer-2
 //
@@ -212,17 +261,18 @@ struct desc {
    struct desc       *next;       // Next defined action for a controller/key with that note value (NULL for end of list)
 };
 
-struct cmdtable{
-   struct desc *desc[128];    // description for Note On/Off and ControllerChange
-   struct desc *pitch;        // description for PitchChanges
-};
+extern struct desc *MidiCommandsTable[129];
+
+extern int midi_debug;
 
 //
 // Layer-1 entry point, called once for all the MIDI devices
 // that have been defined. This is called upon startup by
 // Layer-2 through the function MIDIstartup.
 //
-void register_midi_device(char *name);
+void register_midi_device(int index);
+void close_midi_device(int index);
+void configure_midi_device(gboolean state);
 
 //
 // Layer-2 entry point (called by Layer1)
@@ -235,7 +285,9 @@ void register_midi_device(char *name);
 // for each device description that was successfully read.
 
 void NewMidiEvent(enum MIDIevent event, int channel, int note, int val);
-void MIDIstartup();
+int MIDIstartup(char *filename);
+void MidiAddCommand(int note, struct desc *desc);
+void MidiReleaseCommands();
 
 //
 // Layer-3 entry point (called by Layer2). In Layer-3, all the pihpsdr
diff --git a/midi2.c b/midi2.c
index 50c67438afae13011311a278957e2ba944c627a6..7010074246585047804dffa1d845413c1dbca53c 100644 (file)
--- a/midi2.c
+++ b/midi2.c
@@ -8,6 +8,7 @@
  */
 
 #include <gtk/gtk.h>
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include "MacOS.h"  // emulate clock_gettime on old MacOS systems
 #endif
 
+#include "receiver.h"
+#include "discovered.h"
+#include "adc.h"
+#include "dac.h"
+#include "transmitter.h"
+#include "radio.h"
+#include "main.h"
 #include "midi.h"
+#include "alsa_midi.h"
 
-static double midi_startup_time;
-static int    midi_wait_startup=0;
-
-struct cmdtable MidiCommandsTable;
+struct desc *MidiCommandsTable[129];
 
 void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) {
 
@@ -38,28 +44,14 @@ void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) {
 //now=ts.tv_sec + 1E-9*ts.tv_nsec;
 //g_print("%s:%12.3f:EVENT=%d CHAN=%d NOTE=%d VAL=%d\n",__FUNCTION__,now,event,channel,note,val);
 
-    //
-    // the midi_wait_startup/midi_startup_time mechanism takes care that in the first
-    // second after registering a MIDI device, all incoming MIDI messages are just discarded.
-    // This has been introduced since sometimes "old" MIDI messages are lingering around in the
-    // the system and get delivered immediately after registering the MIDI device.
-    // The midi_wait_startup variable takes care that we do not check the clock again and again
-    // after the first second.
-    //
-    if (midi_wait_startup) {
-      clock_gettime(CLOCK_MONOTONIC, &ts);
-      now=ts.tv_sec + 1E-9*ts.tv_nsec;
-      if (now < midi_startup_time + 1.0) return;
-      midi_wait_startup=0;
-    }
     if (event == MIDI_EVENT_PITCH) {
-       desc=MidiCommandsTable.pitch;
+       desc=MidiCommandsTable[128];
     } else {
-       desc=MidiCommandsTable.desc[note];
+       desc=MidiCommandsTable[note];
     }
-//fprintf(stderr,"MIDI:init DESC=%p\n",desc);
+//g_print("MIDI:init DESC=%p\n",desc);
     while (desc) {
-//fprintf(stderr,"DESC=%p next=%p CHAN=%d EVENT=%d\n", desc,desc->next,desc->channel,desc->event);
+//g_print("DESC=%p next=%p CHAN=%d EVENT=%d\n", desc,desc->next,desc->channel,desc->event);
        if ((desc->channel == channel || desc->channel == -1) && (desc->event == event)) {
            // Found matching entry
            switch (desc->event) {
@@ -81,17 +73,17 @@ void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) {
                          if (delta < desc->delay) break;
                          last_wheel_tp = tp;
                        }
-                       // translate value to direction
+                       // translate value to direction/speed
                        new=0;
-                       if ((val >= desc->vfl1) && (val <= desc->vfl2)) new=-100;
-                       if ((val >= desc-> fl1) && (val <= desc-> fl2)) new=-10;
+                       if ((val >= desc->vfl1) && (val <= desc->vfl2)) new=-16;
+                       if ((val >= desc-> fl1) && (val <= desc-> fl2)) new=-4;
                        if ((val >= desc->lft1) && (val <= desc->lft2)) new=-1;
                        if ((val >= desc->rgt1) && (val <= desc->rgt2)) new= 1;
-                       if ((val >= desc-> fr1) && (val <= desc-> fr2)) new= 10;
-                       if ((val >= desc->vfr1) && (val <= desc->vfr2)) new= 100;
-//                     fprintf(stderr,"WHEEL: val=%d new=%d thrs=%d/%d, %d/%d, %d/%d, %d/%d, %d/%d, %d/%d\n",
-//                                  val, new, desc->vfl1, desc->vfl2, desc->fl1, desc->fl2, desc->lft1, desc->lft2,
-//                                       desc->rgt1, desc->rgt2, desc->fr1, desc->fr2, desc->vfr1, desc->vfr2);
+                       if ((val >= desc-> fr1) && (val <= desc-> fr2)) new= 4;
+                       if ((val >= desc->vfr1) && (val <= desc->vfr2)) new= 16;
+//                     g_print("WHEEL: val=%d new=%d thrs=%d/%d, %d/%d, %d/%d, %d/%d, %d/%d, %d/%d\n",
+//                               val, new, desc->vfl1, desc->vfl2, desc->fl1, desc->fl2, desc->lft1, desc->lft2,
+//                              desc->rgt1, desc->rgt2, desc->fr1, desc->fr2, desc->vfr1, desc->vfr2);
                        if (new != 0) DoTheMidi(desc->action, desc->type, new);
                        last_wheel_action=desc->action;
                    }
@@ -113,101 +105,139 @@ void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) {
     }
     if (!desc) {
       // Nothing found. This is nothing to worry about, but log the key to stderr
-      if (event == MIDI_EVENT_PITCH) fprintf(stderr, "Unassigned PitchBend Value=%d\n", val);
-      if (event == MIDI_EVENT_NOTE ) fprintf(stderr, "Unassigned Key Note=%d Val=%d\n", note, val);
-      if (event == MIDI_EVENT_CTRL ) fprintf(stderr, "Unassigned Controller Ctl=%d Val=%d\n", note, val);
+      if (event == MIDI_EVENT_PITCH) g_print("Unassigned PitchBend Value=%d\n", val);
+      if (event == MIDI_EVENT_NOTE ) g_print("Unassigned Key Note=%d Val=%d\n", note, val);
+      if (event == MIDI_EVENT_CTRL ) g_print("Unassigned Controller Ctl=%d Val=%d\n", note, val);
     }
 }
 
+gchar *midi_types[] = {"NONE","KEY","KNOB/SLIDER","*INVALID*","WHEEL"};
+gchar *midi_events[] = {"NONE","NOTE","CTRL","PITCH"};
+
 /*
  * This data structre connects names as used in the midi.props file with
  * our MIDIaction enum values.
- * Take care that no key word is contained in another one!
- * Example: use "CURRVFO" not "VFO" otherwise there is possibly
- * a match for "VFO" when the key word is "VFOA".
+ *
+ * At some places in the code, it is assumes that ActionTable[i].action == i
+ * so keep the entries strictly in the order the enum is defined, and
+ * add one entry with ACTION_NONE at the end.
  */
 
-static struct {
-  enum MIDIaction action;    // the MIDI action
-  const char *str;           // the key word in the midi.props file
-  int   onoff;               // =1 if action both on press + release
-} ActionTable[] = {
-       { MIDI_ACTION_VFO_A2B,          "A2B",                 0},
-        { MIDI_ACTION_AF_GAIN,         "AFGAIN",              0},
-       { MIDI_ACTION_AGCATTACK,        "AGCATTACK",           0},
-        { MIDI_ACTION_AGC,                     "AGCVAL",              0},
-        { MIDI_ACTION_ANF,                     "ANF",                 0},
-        { MIDI_ACTION_ATT,             "ATT",                 0},
-       { MIDI_ACTION_VFO_B2A,          "B2A",                 0},
-        { MIDI_ACTION_BAND_DOWN,       "BANDDOWN",            0},
-        { MIDI_ACTION_BAND_UP,         "BANDUP",              0},
-        { MIDI_ACTION_COMPRESS,        "COMPRESS",            0},
-       { MIDI_ACTION_CTUN,             "CTUN",                0},
-       { MIDI_ACTION_VFO,              "CURRVFO",             0},
-       { MIDI_ACTION_CWKEY,            "CWKEY",               1},
-       { MIDI_ACTION_CWL,              "CWL",                 1},
-       { MIDI_ACTION_CWR,              "CWR",                 1},
-       { MIDI_ACTION_CWSPEED,          "CWSPEED",             0},
-       { MIDI_ACTION_DIV_COARSEGAIN,   "DIVCOARSEGAIN",       0},
-       { MIDI_ACTION_DIV_COARSEPHASE,  "DIVCOARSEPHASE",      0},
-       { MIDI_ACTION_DIV_FINEGAIN,     "DIVFINEGAIN",         0},
-       { MIDI_ACTION_DIV_FINEPHASE,    "DIVFINEPHASE",        0},
-       { MIDI_ACTION_DIV_GAIN,         "DIVGAIN",             0},
-       { MIDI_ACTION_DIV_PHASE,        "DIVPHASE",            0},
-       { MIDI_ACTION_DIV_TOGGLE,       "DIVTOGGLE",           0},
-       { MIDI_ACTION_DUP,              "DUP",                 0},
-        { MIDI_ACTION_FILTER_DOWN,     "FILTERDOWN",          0},
-        { MIDI_ACTION_FILTER_UP,       "FILTERUP",            0},
-       { MIDI_ACTION_LOCK,             "LOCK",                0},
-        { MIDI_ACTION_MIC_VOLUME,      "MICGAIN",             0},
-       { MIDI_ACTION_MODE_DOWN,        "MODEDOWN",            0},
-       { MIDI_ACTION_MODE_UP,          "MODEUP",              0},
-        { MIDI_ACTION_MOX,                     "MOX",                 0},
-       { MIDI_ACTION_MUTE,             "MUTE",                0},
-       { MIDI_ACTION_NB,               "NOISEBLANKER",        0},
-       { MIDI_ACTION_NR,               "NOISEREDUCTION",      0},
-        { MIDI_ACTION_PAN,             "PAN",                 0},
-        { MIDI_ACTION_PAN_HIGH,        "PANHIGH",             0},
-        { MIDI_ACTION_PAN_LOW,         "PANLOW",              0},
-        { MIDI_ACTION_PRE,             "PREAMP",              0},
-       { MIDI_ACTION_PTTONOFF,         "PTT",                 1},
-       { MIDI_ACTION_PS,               "PURESIGNAL",          0},
-        { MIDI_ACTION_MEM_RECALL_M0,   "RECALLM0",            0},
-        { MIDI_ACTION_MEM_RECALL_M1,   "RECALLM1",            0},
-        { MIDI_ACTION_MEM_RECALL_M2,   "RECALLM2",            0},
-        { MIDI_ACTION_MEM_RECALL_M3,   "RECALLM3",            0},
-        { MIDI_ACTION_MEM_RECALL_M4,   "RECALLM4",            0},
-       { MIDI_ACTION_RF_GAIN,          "RFGAIN",              0},
-        { MIDI_ACTION_TX_DRIVE,        "RFPOWER",             0},
-       { MIDI_ACTION_RIT_CLEAR,        "RITCLEAR",            0},
-       { MIDI_ACTION_RIT_STEP,         "RITSTEP",             0},
-        { MIDI_ACTION_RIT_TOGGLE,      "RITTOGGLE",           0},
-        { MIDI_ACTION_RIT_VAL,         "RITVAL",              0},
-        { MIDI_ACTION_SAT,                     "SAT",                 0},
-        { MIDI_ACTION_SNB,             "SNB",                 0},
-       { MIDI_ACTION_SPLIT,            "SPLIT",               0},
-        { MIDI_ACTION_MEM_STORE_M0,     "STOREM0",             0},
-        { MIDI_ACTION_MEM_STORE_M1,     "STOREM1",             0},
-        { MIDI_ACTION_MEM_STORE_M2,     "STOREM2",             0},
-        { MIDI_ACTION_MEM_STORE_M3,     "STOREM3",             0},
-        { MIDI_ACTION_MEM_STORE_M4,     "STOREM4",             0},
-       { MIDI_ACTION_SWAP_RX,          "SWAPRX",              0},
-       { MIDI_ACTION_SWAP_VFO,         "SWAPVFO",             0},
-        { MIDI_ACTION_TUNE,                    "TUNE",                0},
-        { MIDI_ACTION_VFOA,            "VFOA",                0},
-        { MIDI_ACTION_VFOB,            "VFOB",                0},
-       { MIDI_ACTION_VFO_STEP_UP,      "VFOSTEPUP",           0},
-       { MIDI_ACTION_VFO_STEP_DOWN,    "VFOSTEPDOWN",         0},
-       { MIDI_ACTION_VOX,              "VOX",                 0},
-       { MIDI_ACTION_VOXLEVEL,         "VOXLEVEL",            0},
-       { MIDI_ACTION_XIT_CLEAR,        "XITCLEAR",            0},
-       { MIDI_ACTION_XIT_VAL,          "XITVAL",              0},
-       { MIDI_ACTION_ZOOM,             "ZOOM",                0},
-       { MIDI_ACTION_ZOOM_UP,          "ZOOMUP",              0},
-       { MIDI_ACTION_ZOOM_DOWN,        "ZOOMDOWN",            0},
-        { MIDI_ACTION_NONE,            "NONE",                0}
+ACTION_TABLE ActionTable[] = {
+        { MIDI_ACTION_NONE,            "NONE",                 MIDI_TYPE_KEY|MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,   0},
+        { MIDI_ACTION_VFO_A2B,                 "A2B",                  MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_AF_GAIN,                 "AFGAIN",               MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_AGCATTACK,               "AGCATTACK",            MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_AGC,                     "AGCVAL",               MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+       { MIDI_ACTION_ANF,              "ANF",                  MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_ATT,                     "ATT",                  MIDI_TYPE_KEY|MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,   0},
+        { MIDI_ACTION_VFO_B2A,         "B2A",                  MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_10,                 "BAND10",               MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_12,                 "BAND12",               MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_1240,               "BAND1240",             MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_144,                "BAND144",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_15,                 "BAND15",               MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_160,                "BAND160",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_17,                 "BAND17",               MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_20,                 "BAND20",               MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_220,                "BAND220",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_2300,               "BAND2300",             MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_30,                 "BAND30",               MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_3400,               "BAND3400",             MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_40,                 "BAND40",               MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_430,                "BAND430",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_6,                  "BAND6",                MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_60,                 "BAND60",               MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_70,                 "BAND70",               MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_80,                 "BAND80",               MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_902,                "BAND902",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_AIR,                "BANDAIR",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_DOWN,       "BANDDOWN",             MIDI_TYPE_KEY|MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,   0},
+        { MIDI_ACTION_BAND_GEN,        "BANDGEN",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_BAND_UP,                 "BANDUP",               MIDI_TYPE_KEY|MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,   0},
+        { MIDI_ACTION_BAND_WWV,                "BANDWWV",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_COMPRESS,                "COMPRESS",             MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_CTUN,                    "CTUN",                 MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_VFO,                     "CURRVFO",              MIDI_TYPE_WHEEL,                                0},
+        { MIDI_ACTION_CWKEYER,                 "CW(Keyer)",            MIDI_TYPE_KEY,                                  1},
+        { MIDI_ACTION_CWLEFT,          "CWLEFT",               MIDI_TYPE_KEY,                                  1},
+        { MIDI_ACTION_CWRIGHT,                 "CWRIGHT",              MIDI_TYPE_KEY,                                  1},
+        { MIDI_ACTION_CWSPEED,                 "CWSPEED",              MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_DIV_COARSEGAIN,          "DIVCOARSEGAIN",        MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_DIV_COARSEPHASE,  "DIVCOARSEPHASE",       MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                        0},
+        { MIDI_ACTION_DIV_FINEGAIN,     "DIVFINEGAIN",         MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_DIV_FINEPHASE,    "DIVFINEPHASE",        MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_DIV_GAIN,         "DIVGAIN",             MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_DIV_PHASE,        "DIVPHASE",            MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_DIV_TOGGLE,       "DIVTOGGLE",           MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_DUP,                     "DUP",                  MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_FILTER_DOWN,      "FILTERDOWN",          MIDI_TYPE_KEY|MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,   0},
+        { MIDI_ACTION_FILTER_UP,        "FILTERUP",            MIDI_TYPE_KEY|MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,   0},
+        { MIDI_ACTION_LOCK,                    "LOCK",                 MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MEM_RECALL_M0,    "RECALLM0",            MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MEM_RECALL_M1,    "RECALLM1",            MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MEM_RECALL_M2,    "RECALLM2",            MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MEM_RECALL_M3,    "RECALLM3",            MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MEM_RECALL_M4,    "RECALLM4",            MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MENU_FILTER,      "MENU_FILTER",         MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MENU_MODE,        "MENU_MODE",           MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MIC_VOLUME,       "MICGAIN",             MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_MODE_DOWN,        "MODEDOWN",            MIDI_TYPE_KEY|MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,   0},
+        { MIDI_ACTION_MODE_UP,          "MODEUP",              MIDI_TYPE_KEY|MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,   0},
+        { MIDI_ACTION_MOX,                     "MOX",                  MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MUTE,                    "MUTE",                 MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NB,              "NOISEBLANKER",         MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NR,                      "NOISEREDUCTION",       MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_0,                "NUMPAD0",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_1,                "NUMPAD1",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_2,                "NUMPAD2",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_3,                "NUMPAD3",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_4,                "NUMPAD4",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_5,                "NUMPAD5",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_6,                "NUMPAD6",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_7,                "NUMPAD7",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_8,                "NUMPAD8",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_9,                "NUMPAD9",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_CL,               "NUMPADCL",             MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_NUMPAD_ENTER,            "NUMPADENTER",          MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_PAN,                     "PAN",                  MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_PAN_HIGH,                "PANHIGH",              MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_PAN_LOW,                 "PANLOW",               MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_PRE,                     "PREAMP",               MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_PTTKEYER,                "PTT(Keyer)",           MIDI_TYPE_KEY,                                  1},
+        { MIDI_ACTION_PS,               "PURESIGNAL",          MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_RF_GAIN,                 "RFGAIN",               MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_TX_DRIVE,         "RFPOWER",             MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_RIT_CLEAR,               "RITCLEAR",             MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_RIT_STEP,         "RITSTEP",             MIDI_TYPE_KEY|MIDI_TYPE_WHEEL,                  0},
+        { MIDI_ACTION_RIT_TOGGLE,       "RITTOGGLE",           MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_RIT_VAL,          "RITVAL",              MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_SAT,                     "SAT",                  MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_SNB,              "SNB",                 MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_SPLIT,                   "SPLIT",                MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MEM_STORE_M0,     "STOREM0",                     MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MEM_STORE_M1,     "STOREM1",                     MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MEM_STORE_M2,     "STOREM2",                     MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_MEM_STORE_M3,     "STOREM3",              MIDI_TYPE_KEY,                                 0},
+        { MIDI_ACTION_MEM_STORE_M4,     "STOREM4",              MIDI_TYPE_KEY,                                 0},
+        { MIDI_ACTION_SWAP_RX,          "SWAPRX",              MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_SWAP_VFO,         "SWAPVFO",             MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_TUNE,                    "TUNE",                 MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_VFOA,             "VFOA",                MIDI_TYPE_WHEEL,                                0},
+        { MIDI_ACTION_VFOB,             "VFOB",                MIDI_TYPE_WHEEL,                                0},
+        { MIDI_ACTION_VFO_STEP_UP,      "VFOSTEPUP",           MIDI_TYPE_KEY|MIDI_TYPE_WHEEL,                  0},
+        { MIDI_ACTION_VFO_STEP_DOWN,    "VFOSTEPDOWN",         MIDI_TYPE_KEY|MIDI_TYPE_WHEEL,                  0},
+        { MIDI_ACTION_VOX,              "VOX",                 MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_VOXLEVEL,         "VOXLEVEL",            MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_XIT_CLEAR,               "XITCLEAR",             MIDI_TYPE_KEY,                                  0},
+        { MIDI_ACTION_XIT_VAL,          "XITVAL",              MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_ZOOM,                    "ZOOM",                 MIDI_TYPE_KNOB|MIDI_TYPE_WHEEL,                 0},
+        { MIDI_ACTION_ZOOM_UP,          "ZOOMUP",              MIDI_TYPE_KEY|MIDI_TYPE_WHEEL,                  0},
+        { MIDI_ACTION_ZOOM_DOWN,        "ZOOMDOWN",            MIDI_TYPE_KEY|MIDI_TYPE_WHEEL,                  0},
+        { MIDI_ACTION_LAST,            "NONE",                 MIDI_TYPE_NONE,                                 0},
 };
 
+
 /*
  * Translation from keyword in midi.props file to MIDIaction
  */
@@ -227,12 +257,68 @@ static void keyword2action(char *s, enum MIDIaction *action, int *onoff) {
     *onoff  = 0;
 }
 
+int MIDIstop() {
+  for (int i=0; i<n_midi_devices; i++) {
+    if (midi_devices[i].active) {
+      close_midi_device(i);
+    }
+  }
+  return 0;
+}
+
 /*
- * Here we read in a MIDI description file "midi.def" and fill the MidiCommandsTable
+ * Release data from MidiCommandsTable
+ */
+
+void MidiReleaseCommands() {
+  int i;
+  struct desc *loop, *new;
+  for (i=0; i<129; i++) {
+    loop = MidiCommandsTable[i];
+    while (loop != NULL) {
+      new=loop->next;
+      free(loop);
+      loop = new;
+    }
+    MidiCommandsTable[i]=NULL;
+  }
+}
+
+/*
+ * Add a command to MidiCommandsTable
+ */
+
+void MidiAddCommand(int note, struct desc *desc) {
+  struct desc *loop;
+
+  if (note < 0 || note > 128) return;
+
+  //
+  // Actions with channel == -1 (ANY) must go to the end of the list
+  //
+  if (MidiCommandsTable[note] == NULL) {
+    // initialize linked list
+    MidiCommandsTable[note]=desc;
+  } else if (desc->channel >= 0) {
+    // add to top of the list
+    desc->next = MidiCommandsTable[note];
+    MidiCommandsTable[note]=desc;
+  } else {
+    // add to tail of the list
+    loop = MidiCommandsTable[note];
+    while (loop->next != NULL) {
+      loop = loop->next;
+    }
+    loop->next=desc;
+  }
+}
+
+/*
+ * Here we read in a MIDI description file and fill the MidiCommandsTable
  * data structure
  */
 
-void MIDIstartup() {
+int MIDIstartup(char *filename) {
     FILE *fpin;
     char zeile[255];
     char *cp,*cq;
@@ -246,13 +332,17 @@ void MIDIstartup() {
     enum MIDIevent event;
     int i;
     char c;
-    struct timespec ts;
 
-    for (i=0; i<128; i++) MidiCommandsTable.desc[i]=NULL;
-    MidiCommandsTable.pitch=NULL;
+    MidiReleaseCommands();
 
-    fpin=fopen("midi.props", "r");
-    if (!fpin) return;
+    g_print("%s: %s\n",__FUNCTION__,filename);
+    fpin=fopen(filename, "r");
+
+    g_print("%s: fpin=%p\n",__FUNCTION__,fpin);
+    if (!fpin) {
+      g_print("%s: failed to open MIDI device\n",__FUNCTION__);
+      return -1;
+    }
 
     for (;;) {
       if (fgets(zeile, 255, fpin) == NULL) break;
@@ -277,30 +367,16 @@ void MIDIstartup() {
        cp++;
       }
       
-//fprintf(stderr,"\nMIDI:INP:%s\n",zeile);
-
-      if ((cp = strstr(zeile, "DEVICE="))) {
-        // Delete comments and trailing blanks
-       cq=cp+7;
-       while (*cq != 0 && *cq != '#') cq++;
-       *cq--=0;
-       while (cq > cp+7 && (*cq == ' ' || *cq == '\t')) cq--;
-       *(cq+1)=0;
-//fprintf(stderr,"MIDI:REG:>>>%s<<<\n",cp+7);
-        midi_wait_startup=1;
-        clock_gettime(CLOCK_MONOTONIC, &ts);
-        midi_startup_time=ts.tv_sec + 1E-9*ts.tv_nsec;
-       register_midi_device(cp+7);
-        continue; // nothing more in this line
-      }
+g_print("\n%s:INP:%s\n",__FUNCTION__,zeile);
+
       chan=-1;  // default: any channel
       t1=t3=t5=t7= t9=t11=128;  // range that never occurs
       t2=t4=t6=t8=t10=t12=-1;   // range that never occurs
+      onoff=0;
       event=MIDI_EVENT_NONE;
       type=MIDI_TYPE_NONE;
       key=0;
       delay=0;
-      onoff=0;
 
       //
       // The KEY=, CTRL=, and PITCH= cases are mutually exclusive
@@ -311,18 +387,18 @@ void MIDIstartup() {
         sscanf(cp+4, "%d", &key);
         event=MIDI_EVENT_NOTE;
        type=MIDI_TYPE_KEY;
-//fprintf(stderr,"MIDI:KEY:%d\n", key);
+g_print("%s: MIDI:KEY:%d\n",__FUNCTION__, key);
       }
       if ((cp = strstr(zeile, "CTRL="))) {
         sscanf(cp+5, "%d", &key);
        event=MIDI_EVENT_CTRL;
        type=MIDI_TYPE_KNOB;
-//fprintf(stderr,"MIDI:CTL:%d\n", key);
+g_print("%s: MIDI:CTL:%d\n",__FUNCTION__, key);
       }
       if ((cp = strstr(zeile, "PITCH "))) {
         event=MIDI_EVENT_PITCH;
        type=MIDI_TYPE_KNOB;
-//fprintf(stderr,"MIDI:PITCH\n");
+g_print("%s: MIDI:PITCH\n",__FUNCTION__);
       }
       //
       // If event is still undefined, skip line
@@ -342,16 +418,16 @@ void MIDIstartup() {
         sscanf(cp+5, "%d", &chan);
        chan--;
         if (chan<0 || chan>15) chan=-1;
-//fprintf(stderr,"MIDI:CHA:%d\n",chan);
+g_print("%s:CHAN:%d\n",__FUNCTION__,chan);
       }
       if ((cp = strstr(zeile, "WHEEL")) && (type == MIDI_TYPE_KNOB)) {
        // change type from MIDI_TYPE_KNOB to MIDI_TYPE_WHEEL
         type=MIDI_TYPE_WHEEL;
-//fprintf(stderr,"MIDI:WHEEL\n");
+g_print("%s:WHEEL\n",__FUNCTION__);
       }
       if ((cp = strstr(zeile, "DELAY="))) {
         sscanf(cp+6, "%d", &delay);
-//fprintf(stderr,"MIDI:DELAY:%d\n",delay);
+g_print("%s:DELAY:%d\n",__FUNCTION__,delay);
       }
       if ((cp = strstr(zeile, "THR="))) {
         sscanf(cp+4, "%d %d %d %d %d %d %d %d %d %d %d %d",
@@ -364,7 +440,7 @@ void MIDIstartup() {
         while (*cq != 0 && *cq != '\n' && *cq != ' ' && *cq != '\t') cq++;
        *cq=0;
         keyword2action(cp+7, &action, &onoff);
-//fprintf(stderr,"MIDI:ACTION:%s (%d), onoff=%d\n",cp+7, action, onoff);
+g_print("MIDI:ACTION:%s (%d), onoff=%d\n",cp+7, action, onoff);
       }
       //
       // All data for a descriptor has been read. Construct it!
@@ -395,23 +471,12 @@ void MIDIstartup() {
       //
       if (event == MIDI_EVENT_PITCH) {
 //fprintf(stderr,"MIDI:TAB:Insert desc=%p in PITCH table\n",desc);
-       dp = MidiCommandsTable.pitch;
-       if (dp == NULL) {
-         MidiCommandsTable.pitch = desc;
-       } else {
-         while (dp->next != NULL) dp=dp->next;
-         dp->next=desc;
-       }
+        MidiAddCommand(129, desc);
       }
       if (event == MIDI_EVENT_NOTE || event == MIDI_EVENT_CTRL) {
-//fprintf(stderr,"MIDI:TAB:Insert desc=%p in CMDS[%d] table\n",desc,key);
-       dp = MidiCommandsTable.desc[key];
-       if (dp == NULL) {
-         MidiCommandsTable.desc[key]=desc;
-       } else {
-         while (dp->next != NULL) dp=dp->next;
-         dp->next=desc;
-       }
+g_print("%s:TAB:Insert desc=%p in CMDS[%d] table\n",__FUNCTION__,desc,key);
+        MidiAddCommand(key, desc);
       }
     }
+    return 0;
 }
diff --git a/midi3.c b/midi3.c
index 38ea44ad0f325c190cd26c0c3a482cc1aced31b1..3fc1b055f6ffb4277902840aa87e46c8d6faa5e6 100644 (file)
--- a/midi3.c
+++ b/midi3.c
@@ -39,7 +39,7 @@
 // code below, one can queue the "big switch statement" into the GTK
 // idle queue and exectue all GUI functions directly.
 //
-// However, this is not wanted for CWKEY, CWL and CWR since
+// However, this is not wanted for CWKEYER, CWLEFT and CWRIGHT since
 // these have to be processed with minimal delay (and do not call GUI functions).
 //
 // Therefore, these three cases are already handled in the MIDI callback
@@ -61,7 +61,7 @@ typedef struct _MIDIcmd MIDIcmd;
 static int DoTheRestOfTheMIDI(void *data);
 
 void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
-    if (action == MIDI_ACTION_CWKEY) {
+    if (action == MIDI_ACTION_CWKEYER) {
           //
           // This is a CW key-up/down which uses functions from the keyer
           // that by-pass the interrupt-driven standard action.
@@ -70,7 +70,8 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
           // MIDI using this command.
           //
           // NO BREAK-IN! The keyer has to take care of sending MIDI PTT
-          // on/off messages at appropriate times.
+          // on/off messages at appropriate times, or the operator has to
+          // manually engage/disengage PTT
           //
           // Since this if for immediate key-down, it does not rely on LOCALCW
           //
@@ -85,11 +86,11 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
           return;
     }
 #ifdef LOCALCW
-    if (action == MIDI_ACTION_CWL) {
+    if (action == MIDI_ACTION_CWLEFT) {
          keyer_event(1, val);
          return;
     }
-    if (action == MIDI_ACTION_CWR) {
+    if (action == MIDI_ACTION_CWRIGHT) {
          keyer_event(0, val);
          return;
     }
@@ -109,6 +110,7 @@ int DoTheRestOfTheMIDI(void *data) {
     MIDIcmd *cmd = (MIDIcmd *)data;
    
     int new;
+    int id, c25;
     double dnew;
 
     enum MIDIaction action = cmd->action;
@@ -123,9 +125,7 @@ int DoTheRestOfTheMIDI(void *data) {
     switch (action) {
        /////////////////////////////////////////////////////////// "A2B"
        case MIDI_ACTION_VFO_A2B: // only key supported
-           if (type == MIDI_TYPE_KEY) {
-              vfo_a_to_b();
-           }
+            vfo_a_to_b();
            break;
        /////////////////////////////////////////////////////////// "AFGAIN"
        case MIDI_ACTION_AF_GAIN: // knob or wheel supported
@@ -148,12 +148,10 @@ int DoTheRestOfTheMIDI(void *data) {
        /////////////////////////////////////////////////////////// "AGCATTACK"
        case MIDI_ACTION_AGCATTACK: // only key supported
            // cycle through fast/med/slow AGC attack
-           if (type == MIDI_TYPE_KEY) {
-             new=active_receiver->agc + 1;
-             if (new > AGC_FAST) new=0;
-             active_receiver->agc=new;
-              vfo_update();
-           }
+           new=active_receiver->agc + 1;
+           if (new > AGC_FAST) new=0;
+           active_receiver->agc=new;
+            vfo_update();
            break;
        /////////////////////////////////////////////////////////// "AGCVAL"
        case MIDI_ACTION_AGC: // knob or wheel supported
@@ -176,17 +174,15 @@ int DoTheRestOfTheMIDI(void *data) {
            break;
        /////////////////////////////////////////////////////////// "ANF"
        case MIDI_ACTION_ANF:   // only key supported
-           if (type == MIDI_TYPE_KEY) {
-              if (active_receiver->anf==0) {
-                active_receiver->anf=1;
-                mode_settings[vfo[active_receiver->id].mode].anf=1;
-              } else {
-                active_receiver->snb=0;
-                mode_settings[vfo[active_receiver->id].mode].anf=0;
-              }
-              SetRXAANFRun(active_receiver->id, active_receiver->anf);
-              vfo_update();
-           }
+            if (active_receiver->anf==0) {
+              active_receiver->anf=1;
+              mode_settings[vfo[active_receiver->id].mode].anf=1;
+            } else {
+              active_receiver->snb=0;
+              mode_settings[vfo[active_receiver->id].mode].anf=0;
+            }
+            SetRXAANFRun(active_receiver->id, active_receiver->anf);
+            vfo_update();
            break;
        /////////////////////////////////////////////////////////// "ATT"
        case MIDI_ACTION_ATT:   // Key for ALEX attenuator, wheel or knob for slider
@@ -232,10 +228,87 @@ int DoTheRestOfTheMIDI(void *data) {
            break;
        /////////////////////////////////////////////////////////// "B2A"
        case MIDI_ACTION_VFO_B2A: // only key supported
-           if (type == MIDI_TYPE_KEY) {
-              vfo_b_to_a();
-           }
+            vfo_b_to_a();
            break;
+        /////////////////////////////////////////////////////////// "BANDxxx"
+        case MIDI_ACTION_BAND_10:
+            vfo_band_changed(active_receiver->id, band10);
+            break;
+        case MIDI_ACTION_BAND_12:
+            vfo_band_changed(active_receiver->id, band12);
+            break;
+#ifdef SOAPYSDR
+        case MIDI_ACTION_BAND_1240:
+            vfo_band_changed(active_receiver->id, band1240);
+            break;
+        case MIDI_ACTION_BAND_144:
+            vfo_band_changed(active_receiver->id, band144);
+            break;
+#endif
+        case MIDI_ACTION_BAND_15:
+            vfo_band_changed(active_receiver->id, band15);
+            break;
+        case MIDI_ACTION_BAND_160:
+            vfo_band_changed(active_receiver->id, band160);
+            break;
+        case MIDI_ACTION_BAND_17:
+            vfo_band_changed(active_receiver->id, band17);
+            break;
+        case MIDI_ACTION_BAND_20:
+            vfo_band_changed(active_receiver->id, band20);
+            break;
+#ifdef SOAPYSDR
+        case MIDI_ACTION_BAND_220:
+            vfo_band_changed(active_receiver->id, band220);
+            break;
+        case MIDI_ACTION_BAND_2300:
+            vfo_band_changed(active_receiver->id, band2300);
+            break;
+#endif
+        case MIDI_ACTION_BAND_30:
+            vfo_band_changed(active_receiver->id, band30);
+            break;
+#ifdef SOAPYSDR
+        case MIDI_ACTION_BAND_3400:
+            vfo_band_changed(active_receiver->id, band3400);
+            break;
+#endif
+        case MIDI_ACTION_BAND_40:
+            vfo_band_changed(active_receiver->id, band40);
+            break;
+#ifdef SOAPYSDR
+        case MIDI_ACTION_BAND_430:
+            vfo_band_changed(active_receiver->id, band430);
+            break;
+#endif
+        case MIDI_ACTION_BAND_6:
+            vfo_band_changed(active_receiver->id, band6);
+            break;
+        case MIDI_ACTION_BAND_60:
+            vfo_band_changed(active_receiver->id, band60);
+            break;
+#ifdef SOAPYSDR
+        case MIDI_ACTION_BAND_70:
+            vfo_band_changed(active_receiver->id, band70);
+            break;
+#endif
+        case MIDI_ACTION_BAND_80:
+            vfo_band_changed(active_receiver->id, band80);
+            break;
+#ifdef SOAPYSDR
+        case MIDI_ACTION_BAND_902:
+            vfo_band_changed(active_receiver->id, band902);
+            break;
+        case MIDI_ACTION_BAND_AIR:
+            vfo_band_changed(active_receiver->id, bandAIR);
+            break;
+#endif
+        case MIDI_ACTION_BAND_GEN:
+            vfo_band_changed(active_receiver->id, bandGen);
+            break;
+        case MIDI_ACTION_BAND_WWV:
+            vfo_band_changed(active_receiver->id, bandWWV);
+            break;
        /////////////////////////////////////////////////////////// "BANDDOWN"
        /////////////////////////////////////////////////////////// "BANDUP"
        case MIDI_ACTION_BAND_DOWN:
@@ -296,21 +369,17 @@ int DoTheRestOfTheMIDI(void *data) {
        /////////////////////////////////////////////////////////// "CTUN"
        case MIDI_ACTION_CTUN: // only key supported
            // toggle CTUN
-           if (type == MIDI_TYPE_KEY) {
-              int id=active_receiver->id;
-              vfo[id].ctun=vfo[id].ctun==1?0:1;
-              if(!vfo[id].ctun) {
-                vfo[id].offset=0;
-              }
-              vfo[id].ctun_frequency=vfo[id].frequency;
-              set_offset(receiver[id],vfo[id].offset);
-           }
+            id=active_receiver->id;
+            vfo[id].ctun=vfo[id].ctun==1?0:1;
+            if(!vfo[id].ctun) {
+              vfo[id].offset=0;
+            }
+            vfo[id].ctun_frequency=vfo[id].frequency;
+            set_offset(receiver[id],vfo[id].offset);
            break;
        /////////////////////////////////////////////////////////// "CURRVFO"
        case MIDI_ACTION_VFO: // only wheel supported
-           if (type == MIDI_TYPE_WHEEL && !locked) {
-                vfo_step(val);
-           }
+            vfo_step(val);
            break;
        /////////////////////////////////////////////////////////// "CWSPEED"
        case MIDI_ACTION_CWSPEED: // knob or wheel
@@ -408,11 +477,9 @@ int DoTheRestOfTheMIDI(void *data) {
             break;
         /////////////////////////////////////////////////////////// "DIVTOGGLE"
         case MIDI_ACTION_DIV_TOGGLE:   // only key supported
-            if (type == MIDI_TYPE_KEY) {
-                // enable/disable DIVERSITY
-                diversity_enabled = diversity_enabled ? 0 : 1;
-                vfo_update();
-            }
+            // enable/disable DIVERSITY
+            diversity_enabled = diversity_enabled ? 0 : 1;
+            vfo_update();
             break;
        /////////////////////////////////////////////////////////// "DUP"
         case MIDI_ACTION_DUP:
@@ -453,12 +520,18 @@ int DoTheRestOfTheMIDI(void *data) {
               vfo_filter_changed(new);
            }
            break;
+        /////////////////////////////////////////////////////////// "MENU_FILTER"
+        case MIDI_ACTION_MENU_FILTER:
+            start_filter();
+            break;
+        /////////////////////////////////////////////////////////// "MENU_MODE"
+        case MIDI_ACTION_MENU_MODE:
+            start_mode();
+            break;
        /////////////////////////////////////////////////////////// "LOCK"
        case MIDI_ACTION_LOCK: // only key supported
-           if (type == MIDI_TYPE_KEY) {
-             locked=!locked;
-              vfo_update();
-           }
+           locked=!locked;
+            vfo_update();
            break;
        /////////////////////////////////////////////////////////// "MICGAIN"
        case MIDI_ACTION_MIC_VOLUME: // knob or wheel supported
@@ -511,54 +584,86 @@ int DoTheRestOfTheMIDI(void *data) {
        /////////////////////////////////////////////////////////// "MOX"
        case MIDI_ACTION_MOX: // only key supported
            // Note this toggles the PTT state without knowing the
-            // actual state. See MIDI_ACTION_PTTONOFF for actually
+            // actual state. See MIDI_ACTION_PTTKEYER for actually
             // *setting* PTT
-           if (type == MIDI_TYPE_KEY && can_transmit) {
+           if (can_transmit) {
                new = !mox;
                 mox_update(new);
            }
            break;    
         /////////////////////////////////////////////////////////// "MUTE"
         case MIDI_ACTION_MUTE:
-            if (type == MIDI_TYPE_KEY) {
-              active_receiver->mute_radio=!active_receiver->mute_radio;
-           }
+            active_receiver->mute_radio=!active_receiver->mute_radio;
             break;
        /////////////////////////////////////////////////////////// "NOISEBLANKER"
        case MIDI_ACTION_NB: // only key supported
            // cycle through NoiseBlanker settings: OFF, NB, NB2
-            if (type == MIDI_TYPE_KEY) {
-             if (active_receiver->nb) {
-               active_receiver->nb = 0;
-               active_receiver->nb2= 1;
-             } else if (active_receiver->nb2) {
-               active_receiver->nb = 0;
-               active_receiver->nb2= 0;
-             } else {
-               active_receiver->nb = 1;
-               active_receiver->nb2= 0;
-             }
-              vfo_update();
+           if (active_receiver->nb) {
+             active_receiver->nb = 0;
+             active_receiver->nb2= 1;
+           } else if (active_receiver->nb2) {
+             active_receiver->nb = 0;
+             active_receiver->nb2= 0;
+           } else {
+             active_receiver->nb = 1;
+             active_receiver->nb2= 0;
            }
+           update_noise();
+            vfo_update();
            break;
        /////////////////////////////////////////////////////////// "NOISEREDUCTION"
        case MIDI_ACTION_NR: // only key supported
            // cycle through NoiseReduction settings: OFF, NR1, NR2
-           if (type == MIDI_TYPE_KEY) {
-             if (active_receiver->nr) {
-               active_receiver->nr = 0;
-               active_receiver->nr2= 1;
-             } else if (active_receiver->nr2) {
-               active_receiver->nr = 0;
-               active_receiver->nr2= 0;
-             } else {
-               active_receiver->nr = 1;
-               active_receiver->nr2= 0;
-             }
-              update_noise();
-              vfo_update();
+           if (active_receiver->nr) {
+             active_receiver->nr = 0;
+             active_receiver->nr2= 1;
+           } else if (active_receiver->nr2) {
+             active_receiver->nr = 0;
+             active_receiver->nr2= 0;
+           } else {
+             active_receiver->nr = 1;
+             active_receiver->nr2= 0;
            }
+            update_noise();
+            vfo_update();
            break;
+        /////////////////////////////////////////////////////////// "NUMPADxx"
+        case MIDI_ACTION_NUMPAD_0:
+            num_pad(0);
+            break;
+        case MIDI_ACTION_NUMPAD_1:
+            num_pad(1);
+            break;
+        case MIDI_ACTION_NUMPAD_2:
+            num_pad(2);
+            break;
+        case MIDI_ACTION_NUMPAD_3:
+            num_pad(3);
+            break;
+        case MIDI_ACTION_NUMPAD_4:
+            num_pad(4);
+            break;
+        case MIDI_ACTION_NUMPAD_5:
+            num_pad(5);
+            break;
+        case MIDI_ACTION_NUMPAD_6:
+            num_pad(6);
+            break;
+        case MIDI_ACTION_NUMPAD_7:
+            num_pad(7);
+            break;
+        case MIDI_ACTION_NUMPAD_8:
+            num_pad(9);
+            break;
+        case MIDI_ACTION_NUMPAD_9:
+            num_pad(9);
+            break;
+        case MIDI_ACTION_NUMPAD_CL:
+            num_pad(-1);
+            break;
+        case MIDI_ACTION_NUMPAD_ENTER:
+            num_pad(-2);
+            break;
        /////////////////////////////////////////////////////////// "PAN"
         case MIDI_ACTION_PAN:  // wheel and knob
            switch (type) {
@@ -635,41 +740,39 @@ int DoTheRestOfTheMIDI(void *data) {
            break;
        /////////////////////////////////////////////////////////// "PREAMP"
        case MIDI_ACTION_PRE:   // only key supported
-           if (type == MIDI_TYPE_KEY) {
-               //
-               // Normally on/off, but for CHARLY25, cycle through three
-               // possible states. Current HPSDR hardware does no have
-               // switch'able preamps.
-               //
-               int c25= (filter_board == CHARLY25);
-               new = active_receiver->preamp + active_receiver->dither;
-               new++;
-               if (c25) {
-                 if (new >2) new=0;
-               } else {
-                 if (new >1) new=0;
-               }
-               switch (new) {
-                   case 0:
-                       active_receiver->preamp=0;
-                       if (c25) active_receiver->dither=0;
-                       break;
-                   case 1:
-                       active_receiver->preamp=1;
-                       if (c25) active_receiver->dither=0;
-                       break;
-                   case 2:
-                       active_receiver->preamp=1;
-                       if (c25) active_receiver->dither=1;
-                       break;
-               }
-                update_att_preamp();
+           //
+           // Normally on/off, but for CHARLY25, cycle through three
+           // possible states. Current HPSDR hardware does no have
+           // switch'able preamps.
+           //
+           c25= (filter_board == CHARLY25);
+           new = active_receiver->preamp + active_receiver->dither;
+           new++;
+           if (c25) {
+             if (new >2) new=0;
+           } else {
+             if (new >1) new=0;
+           }
+           switch (new) {
+             case 0:
+               active_receiver->preamp=0;
+               if (c25) active_receiver->dither=0;
+               break;
+             case 1:
+               active_receiver->preamp=1;
+               if (c25) active_receiver->dither=0;
+               break;
+             case 2:
+               active_receiver->preamp=1;
+               if (c25) active_receiver->dither=1;
+               break;
            }
+            update_att_preamp();
            break;
-       /////////////////////////////////////////////////////////// "PTTONOFF"
-        case MIDI_ACTION_PTTONOFF:  // key only
+       /////////////////////////////////////////////////////////// "PTT(Keyer)"
+        case MIDI_ACTION_PTTKEYER:  // key only
             // always use with "ONOFF"
-           if (type == MIDI_TYPE_KEY && can_transmit) {
+           if (can_transmit) {
                 mox_update(val);
            }
            break;    
@@ -677,27 +780,27 @@ int DoTheRestOfTheMIDI(void *data) {
        case MIDI_ACTION_PS: // only key supported
 #ifdef PURESIGNAL
            // toggle PURESIGNAL
-           if (type == MIDI_TYPE_KEY) {
-              if (can_transmit) {
-               new=!(transmitter->puresignal);
-                tx_set_ps(transmitter, new);
-              }
-           }
+            if (can_transmit) {
+             new=!(transmitter->puresignal);
+              tx_set_ps(transmitter, new);
+            }
 #endif
            break;
-       /////////////////////////////////////////////////////////// "RECALLM[0-4]"
+       /////////////////////////////////////////////////////////// "RECALLMx"
        case MIDI_ACTION_MEM_RECALL_M0:
+            recall_memory_slot(0);
+            break;
        case MIDI_ACTION_MEM_RECALL_M1:
+            recall_memory_slot(1);
+            break;
        case MIDI_ACTION_MEM_RECALL_M2:
+            recall_memory_slot(2);
+            break;
        case MIDI_ACTION_MEM_RECALL_M3:
+            recall_memory_slot(3);
+            break;
        case MIDI_ACTION_MEM_RECALL_M4:
-            //
-           // only key supported
-            //
-            if (type == MIDI_TYPE_KEY) {
-                new = action - MIDI_ACTION_MEM_RECALL_M0;
-                recall_memory_slot(new);
-           }
+            recall_memory_slot(4);
             break;
        /////////////////////////////////////////////////////////// "RFGAIN"
         case MIDI_ACTION_RF_GAIN: // knob or wheel supported
@@ -730,11 +833,10 @@ int DoTheRestOfTheMIDI(void *data) {
            break;
        /////////////////////////////////////////////////////////// "RITCLEAR"
        case MIDI_ACTION_RIT_CLEAR:       // only key supported
-           if (type == MIDI_TYPE_KEY) {
-             // clear RIT value
-             vfo[active_receiver->id].rit = new;
-              vfo_update();
-           }
+           // clear RIT value
+           vfo[active_receiver->id].rit = new;
+            vfo_update();
+           break;
        /////////////////////////////////////////////////////////// "RITSTEP"
         case MIDI_ACTION_RIT_STEP: // key or wheel supported
             // This cycles between RIT increments 1, 10, 100, 1, 10, 100, ...
@@ -761,12 +863,10 @@ int DoTheRestOfTheMIDI(void *data) {
             break;
        /////////////////////////////////////////////////////////// "RITTOGGLE"
        case MIDI_ACTION_RIT_TOGGLE:  // only key supported
-           if (type == MIDI_TYPE_KEY) {
-               // enable/disable RIT
-               new=vfo[active_receiver->id].rit_enabled;
-               vfo[active_receiver->id].rit_enabled = new ? 0 : 1;
-                vfo_update();
-           }
+           // enable/disable RIT
+           new=vfo[active_receiver->id].rit_enabled;
+           vfo[active_receiver->id].rit_enabled = new ? 0 : 1;
+            vfo_update();
            break;
        /////////////////////////////////////////////////////////// "RITVAL"
        case MIDI_ACTION_RIT_VAL:       // wheel or knob
@@ -811,42 +911,40 @@ int DoTheRestOfTheMIDI(void *data) {
             break;
        /////////////////////////////////////////////////////////// "SNB"
        case MIDI_ACTION_SNB:   // only key supported
-           if (type == MIDI_TYPE_KEY) {
-              if(active_receiver->snb==0) {
-                active_receiver->snb=1;
-                mode_settings[vfo[active_receiver->id].mode].snb=1;
-              } else {
-                active_receiver->snb=0;
-                mode_settings[vfo[active_receiver->id].mode].snb=0;
-              }
-              update_noise();
-           }
+            if(active_receiver->snb==0) {
+              active_receiver->snb=1;
+              mode_settings[vfo[active_receiver->id].mode].snb=1;
+            } else {
+              active_receiver->snb=0;
+              mode_settings[vfo[active_receiver->id].mode].snb=0;
+            }
+            update_noise();
            break;
        /////////////////////////////////////////////////////////// "SPLIT"
        case MIDI_ACTION_SPLIT: // only key supported
            // toggle split mode
-           if (type == MIDI_TYPE_KEY) {
-              new= split ? 0:1;
-              set_split(new);
-           }
+            new= split ? 0:1;
+            set_split(new);
            break;
-       /////////////////////////////////////////////////////////// "STOREM[0-4]"
+       /////////////////////////////////////////////////////////// "STOREMx"
        case MIDI_ACTION_MEM_STORE_M0:
+            store_memory_slot(0);
+           break;
        case MIDI_ACTION_MEM_STORE_M1:
+            store_memory_slot(1);
+           break;
        case MIDI_ACTION_MEM_STORE_M2:
+            store_memory_slot(2);
+           break;
        case MIDI_ACTION_MEM_STORE_M3:
+            store_memory_slot(3);
+           break;
        case MIDI_ACTION_MEM_STORE_M4:
-            //
-           // only key supported
-            //
-            if (type == MIDI_TYPE_KEY) {
-                new = action - MIDI_ACTION_MEM_STORE_M0;
-                store_memory_slot(new);
-           }
-            break;
+            store_memory_slot(4);
+           break;
        /////////////////////////////////////////////////////////// "SWAPRX"
        case MIDI_ACTION_SWAP_RX:       // only key supported
-           if (type == MIDI_TYPE_KEY && receivers == 2) {
+           if (receivers == 2) {
                new=active_receiver->id;        // 0 or 1
                new= (new == 1) ? 0 : 1;        // id of currently inactive receiver
                active_receiver=receiver[new];
@@ -857,13 +955,11 @@ int DoTheRestOfTheMIDI(void *data) {
            break;    
        /////////////////////////////////////////////////////////// "SWAPVFO"
        case MIDI_ACTION_SWAP_VFO:      // only key supported
-           if (type == MIDI_TYPE_KEY) {
-                vfo_a_swap_b();
-           }
+            vfo_a_swap_b();
            break;    
        /////////////////////////////////////////////////////////// "TUNE"
        case MIDI_ACTION_TUNE: // only key supported
-           if (type == MIDI_TYPE_KEY && can_transmit) {
+           if (can_transmit) {
                new = !tune;
                 tune_update(new);
            }
@@ -872,7 +968,7 @@ int DoTheRestOfTheMIDI(void *data) {
        /////////////////////////////////////////////////////////// "VFOB"
        case MIDI_ACTION_VFOA: // only wheel supported
        case MIDI_ACTION_VFOB: // only wheel supported
-           if (type == MIDI_TYPE_WHEEL && !locked) {
+           if (!locked) {
                new = (action == MIDI_ACTION_VFOA) ? 0 : 1;
                 vfo_id_step(new, val);
            }
@@ -884,50 +980,25 @@ int DoTheRestOfTheMIDI(void *data) {
            switch (type) {
              case MIDI_TYPE_KEY:
                new =  (action == MIDI_ACTION_VFO_STEP_UP) ? 1 : -1;
+                update_vfo_step(new);
                break;
              case MIDI_TYPE_WHEEL:
                new = (val > 0) ? 1 : -1;
+                update_vfo_step(new);
                break;
              default:
                // do nothing
                // we should not come here anyway
-                new = 0;
-               break;
+                break;
            }
-            if (new != 0) {
-              //
-              // locate where the current step size is located in our table
-              //
-              int i=0;
-              while(steps[i]!=step && steps[i]!=0) {
-                i++;
-              }
-              if(steps[i]!=0) {
-                // found. current step size is at position #i
-                if (new>0) {
-                  // next higher step size, if not yet at end of list
-                  i++;
-                  if(steps[i]!=0) {
-                    step=steps[i];
-                  }
-                } else {
-                  // next lower step size, if not yet at end of list
-                  i--;
-                  if(i>=0) {
-                    step=steps[i];
-                  }
-                }
-              }
-              vfo_update();
-            }
             break;
        /////////////////////////////////////////////////////////// "VOX"
        case MIDI_ACTION_VOX: // only key supported
            // toggle VOX
-           if (type == MIDI_TYPE_KEY) {
+            if (can_transmit) {
              vox_enabled = !vox_enabled;
               vfo_update();
-           }
+            }
            break;
        /////////////////////////////////////////////////////////// "VOXLEVEL"
        case MIDI_ACTION_VOXLEVEL: // knob or wheel supported
@@ -951,13 +1022,11 @@ int DoTheRestOfTheMIDI(void *data) {
            break;
        /////////////////////////////////////////////////////////// "XITCLEAR"
         case MIDI_ACTION_XIT_CLEAR:  // only key supported
-            if (type == MIDI_TYPE_KEY) {
-                // this clears the XIT value and disables XIT
-                if(can_transmit) {
-                  transmitter->xit = 0;
-                  transmitter->xit_enabled = 0;
-                  vfo_update();
-                }
+            // this clears the XIT value and disables XIT
+            if(can_transmit) {
+              transmitter->xit = 0;
+              transmitter->xit_enabled = 0;
+              vfo_update();
             }
             break;
        /////////////////////////////////////////////////////////// "XITVAL"
diff --git a/midi_menu.c b/midi_menu.c
new file mode 100644 (file)
index 0000000..3c5c38e
--- /dev/null
@@ -0,0 +1,1782 @@
+/* Copyright (C)
+* 2020 - John Melton, G0ORX/N6LYT
+*
+* 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*
+*/
+
+#include <gtk/gtk.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+
+#include "discovered.h"
+#include "mode.h"
+#include "filter.h"
+#include "band.h"
+#include "receiver.h"
+#include "transmitter.h"
+#include "receiver.h"
+#include "adc.h"
+#include "dac.h"
+#include "radio.h"
+#include "midi.h"
+#include "alsa_midi.h"
+#include "new_menu.h"
+#include "midi_menu.h"
+#include "property.h"
+
+enum {
+  EVENT_COLUMN,
+  CHANNEL_COLUMN,
+  NOTE_COLUMN,
+  TYPE_COLUMN,
+  ACTION_COLUMN,
+  N_COLUMNS
+};
+
+static GtkWidget *parent_window=NULL;
+static GtkWidget *menu_b=NULL;
+static GtkWidget *dialog=NULL;
+
+static GtkWidget *midi_enable_b;
+
+static GtkListStore *store;
+static GtkWidget *view;
+static GtkWidget *scrolled_window=NULL;
+static gulong selection_signal_id;
+GtkTreeSelection *selection;
+static GtkTreeModel *model;
+static GtkTreeIter iter;
+struct desc *current_cmd;
+
+static GtkWidget *filename;
+
+static GtkWidget *newEvent;
+static GtkWidget *newChannel;
+static GtkWidget *newNote;
+static GtkWidget *newVal;
+static GtkWidget *newType;
+static GtkWidget *newMin;
+static GtkWidget *newMax;
+static GtkWidget *newAction;
+static GtkWidget *configure_b;
+static GtkWidget *any_b;
+static GtkWidget *add_b;
+static GtkWidget *update_b;
+static GtkWidget *delete_b;
+static GtkWidget *device_b[MAX_MIDI_DEVICES];
+
+static enum MIDIevent thisEvent=MIDI_EVENT_NONE;
+static int thisChannel;
+static int thisNote;
+static int thisVal;
+static int thisMin;
+static int thisMax;
+static int thisDelay;
+static int thisVfl1, thisVfl2;
+static int thisFl1,  thisFl2;
+static int thisLft1, thisLft2;
+static int thisRgt1, thisRgt2;
+static int thisFr1,  thisFr2;
+static int thisVfr1, thisVfr2;
+
+static GtkWidget *WheelContainer;
+static GtkWidget *set_delay;
+static GtkWidget *set_vfl1, *set_vfl2;
+static GtkWidget *set_fl1,  *set_fl2;
+static GtkWidget *set_lft1, *set_lft2;
+static GtkWidget *set_rgt1, *set_rgt2;
+static GtkWidget *set_fr1,  *set_fr2;
+static GtkWidget *set_vfr1, *set_vfr2;
+
+static enum MIDItype thisType;
+static enum MIDIaction thisAction;
+
+static gboolean accept_any=FALSE;
+
+enum {
+  UPDATE_NEW,
+  UPDATE_CURRENT,
+  UPDATE_EXISTING
+};
+
+static int update(void *data);
+static void load_store();
+static void add_store(int key,struct desc *cmd);
+
+static void cleanup() {
+  configure_midi_device(FALSE);
+  if(dialog!=NULL) {
+    gtk_widget_destroy(dialog);
+    dialog=NULL;
+    sub_menu=NULL;
+  }
+}
+
+static gboolean close_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
+  cleanup();
+  return TRUE;
+}
+
+static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
+  cleanup();
+  return FALSE;
+}
+
+static void device_cb(GtkWidget *widget, gpointer data) {
+  int index=GPOINTER_TO_INT(data);
+  int val=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+  if (val == 1) {
+    register_midi_device(index);
+  } else {
+    close_midi_device(index);
+  }
+  // take care button remains un-checked if opening failed
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), midi_devices[index].active);
+}
+
+static void configure_cb(GtkWidget *widget, gpointer data) {
+  gboolean conf=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+  configure_midi_device(conf);
+  if (conf) {
+    gtk_widget_show(any_b);
+  } else {
+    gtk_widget_hide(any_b);
+  }
+}
+
+static void any_cb(GtkWidget *widget, gpointer data) {
+  gboolean conf=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+  accept_any = conf;
+}
+
+static void update_wheelparams(gpointer user_data) {
+  //
+  // Task: show or hide WheelContainer depending on whether
+  //       thre current type is a wheel. If it is a wheel,
+  //       set spin buttons to current values.
+  //
+  if (thisType==MIDI_TYPE_WHEEL) {
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_delay),(double) thisDelay);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_vfl1 ),(double) thisVfl1 );
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_vfl2 ),(double) thisVfl2 );
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_fl1  ),(double) thisFl1  );
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_fl2  ),(double) thisFl2  );
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_lft1 ),(double) thisLft1 );
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_lft2 ),(double) thisLft2 );
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_rgt1 ),(double) thisRgt1 );
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_rgt2 ),(double) thisRgt2 );
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_fr1  ),(double) thisFr1  );
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_fr2  ),(double) thisFr2  );
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_vfr1 ),(double) thisVfr1 );
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_vfr2 ),(double) thisVfr2 );
+    gtk_widget_show(WheelContainer);
+  } else {
+    gtk_widget_hide(WheelContainer);
+  }
+}
+
+static void type_changed_cb(GtkWidget *widget, gpointer data) {
+  int i=0;
+  int j=0;
+
+  // update actions available for the type
+  gchar *type=gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(widget));
+
+  g_print("%s: type=%s action=%d\n",__FUNCTION__,type,thisAction);
+  gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(newAction));
+  if(type==NULL) {
+    // leave empty
+    gtk_combo_box_set_active (GTK_COMBO_BOX(newAction),0);
+  } else  if (strcmp(type,"NONE")==0) {
+    thisType=MIDI_TYPE_NONE;
+    gtk_combo_box_set_active (GTK_COMBO_BOX(newAction),0);
+  } else if(strcmp(type,"KEY")==0) {
+    thisType=MIDI_TYPE_KEY;
+    // add all the Key actions
+    i=0;
+    j=0;
+    while(ActionTable[i].action!=MIDI_ACTION_LAST) {
+      if(ActionTable[i].type&MIDI_TYPE_KEY) {
+        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newAction),NULL,ActionTable[i].str);
+       if(ActionTable[i].action==thisAction) {
+          gtk_combo_box_set_active(GTK_COMBO_BOX(newAction),j);
+       }
+       j++;
+      }
+      i++;
+    }
+  } else if(strcmp(type,"KNOB/SLIDER")==0) {
+    thisType=MIDI_TYPE_KNOB;
+    // add all the Knob actions
+    while(ActionTable[i].action!=MIDI_ACTION_LAST) {
+      if(ActionTable[i].type&MIDI_TYPE_KNOB) {
+        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newAction),NULL,ActionTable[i].str);
+       if(ActionTable[i].action==thisAction) {
+          gtk_combo_box_set_active (GTK_COMBO_BOX(newAction),j);
+       }
+       j++;
+      }
+      i++;
+    }
+  } else if(strcmp(type,"WHEEL")==0) {
+    thisType=MIDI_TYPE_WHEEL;
+    // add all the Wheel actions
+    while(ActionTable[i].action!=MIDI_ACTION_LAST) {
+      if(ActionTable[i].type&MIDI_TYPE_WHEEL || ActionTable[i].type&MIDI_TYPE_KNOB) {
+        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newAction),NULL,ActionTable[i].str);
+       if(ActionTable[i].action==thisAction) {
+          gtk_combo_box_set_active (GTK_COMBO_BOX(newAction),j);
+       }
+       j++;
+      }
+      i++;
+    }
+  }
+  update_wheelparams(NULL);
+}
+
+static void row_inserted_cb(GtkTreeModel *tree_model,GtkTreePath *path, GtkTreeIter *iter,gpointer user_data) {
+  //g_print("%s\n",__FUNCTION__);
+  gtk_tree_view_set_cursor(GTK_TREE_VIEW(view),path,NULL,FALSE);
+}
+
+
+static void tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data) {
+  char *str_event;
+  char *str_channel;
+  char *str_note;
+  char *str_type;
+  char *str_action;
+
+  //g_print("%s\n",__FUNCTION__);
+  //if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(configure_b))) {
+    if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+      gtk_tree_model_get(model, &iter, EVENT_COLUMN, &str_event, -1);
+      gtk_tree_model_get(model, &iter, CHANNEL_COLUMN, &str_channel, -1);
+      gtk_tree_model_get(model, &iter, NOTE_COLUMN, &str_note, -1);
+      gtk_tree_model_get(model, &iter, TYPE_COLUMN, &str_type, -1);
+      gtk_tree_model_get(model, &iter, ACTION_COLUMN, &str_action, -1);
+
+      g_print("%s: %s %s %s %s %s\n",__FUNCTION__,str_event,str_channel,str_note,str_type,str_action);
+
+      if(str_event!=NULL && str_channel!=NULL && str_note!=NULL && str_type!=NULL && str_action!=NULL) {
+
+        if(strcmp(str_event,"CTRL")==0) {
+          thisEvent=MIDI_EVENT_CTRL;
+        } else if(strcmp(str_event,"PITCH")==0) {
+          thisEvent=MIDI_EVENT_PITCH;
+        } else if(strcmp(str_event,"NOTE")==0) {
+          thisEvent=MIDI_EVENT_NOTE;
+        } else {
+          thisEvent=MIDI_EVENT_NONE;
+        }
+        if (!strncmp(str_channel,"Any", 3)) {
+          thisChannel=-1;
+        } else {
+          thisChannel=atoi(str_channel);
+        }
+        thisNote=atoi(str_note);
+        thisVal=0;
+        thisMin=0;
+        thisMax=0;
+        if(strcmp(str_type,"KEY")==0) {
+          thisType=MIDI_TYPE_KEY;
+        } else if(strcmp(str_type,"KNOB/SLIDER")==0) {
+          thisType=MIDI_TYPE_KNOB;
+        } else if(strcmp(str_type,"WHEEL")==0) {
+          thisType=MIDI_TYPE_WHEEL;
+        } else {
+          thisType=MIDI_TYPE_NONE;
+        }
+        thisAction=MIDI_ACTION_NONE;
+        int i=0;
+        while(ActionTable[i].action!=MIDI_ACTION_LAST) {
+          if(strcmp(ActionTable[i].str,str_action)==0) {
+            thisAction=ActionTable[i].action;
+            break;
+          }
+          i++;
+        }
+        g_idle_add(update,GINT_TO_POINTER(UPDATE_EXISTING));
+      }
+    }
+  //}
+}
+
+static void find_current_cmd() {
+  struct desc *cmd;
+  g_print("%s: Note=%d Chan=%d Type=%d Action=%d\n",__FUNCTION__, thisNote, thisChannel, thisType, thisAction);
+  cmd=MidiCommandsTable[thisNote];
+  while(cmd!=NULL) {
+    if((cmd->channel==thisChannel) && cmd->type==thisType && cmd->action==thisAction) {
+      g_print("%s: found cmd %p\n",__FUNCTION__,cmd);
+      break;
+    }
+    cmd=cmd->next;
+  }
+  current_cmd=cmd;  // NULL if not found
+}
+
+static void wheelparam_cb(GtkWidget *widget, gpointer user_data) {
+  int what = GPOINTER_TO_INT(user_data);
+  int val=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+  int newval=val;
+
+  if (thisType != MIDI_TYPE_WHEEL) {
+    // we should never arrive here
+    return;
+  }
+  switch (what) {
+      case 1:  // Delay
+        thisDelay=newval;
+        break;
+     case 2:  // Very fast Left 1
+        if (newval > thisVfl2) newval=thisVfl2;
+        thisVfl1=newval;
+        break;
+     case 3:  // Very fast Left 2
+        if (newval < thisVfl1) newval=thisVfl1;
+        thisVfl2=newval;
+        break;
+     case 4:  // Fast Left 1
+        if (newval > thisFl2) newval=thisFl2;
+        thisFl1=newval;
+        break;
+     case 5:  // Fast Left 2
+        if (newval < thisFl1) newval=thisFl1;
+        thisFl2=newval;
+        break;
+     case 6:  // Left 1
+        if (newval > thisLft2) newval=thisLft2;
+        thisLft1=newval;
+        break;
+     case 7:  // Left 2
+        if (newval < thisLft1) newval=thisLft1;
+        thisLft2=newval;
+        break;
+     case 8:  // Right 1
+        if (newval > thisRgt2) newval=thisRgt2;
+        thisRgt1=newval;
+        break;
+     case 9:  // Right 2
+        if (newval < thisRgt1) newval=thisRgt1;
+        thisRgt2=newval;
+        break;
+     case 10:  // Fast Right 1
+        if (newval > thisFr2) newval=thisFr2;
+        thisFr1=newval;
+        break;
+     case 11:  // Fast Right2 
+        if (newval < thisFr1) newval=thisFr1;
+        thisFr2=newval;
+        break;
+     case 12:  // Very fast Right 1
+        if (newval > thisVfr2) newval=thisVfr2;
+        thisVfr1=newval;
+        break;
+     case 13:  // Very fast Right 2
+        if (newval < thisVfr1) newval=thisVfr1;
+        thisVfr2=newval;
+        break;
+  }
+  //
+  // If we have changed the value because we kept thisVfl2 >= thisVfl1 etc,
+  // update the spin button
+  //
+  gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),(double) newval);
+}
+
+static void clear_cb(GtkWidget *widget,gpointer user_data) {
+
+  g_signal_handler_block(G_OBJECT(selection), selection_signal_id);
+  gtk_list_store_clear(store);
+  MidiReleaseCommands();  
+  g_signal_handler_unblock(G_OBJECT(selection), selection_signal_id);
+}
+
+static void save_cb(GtkWidget *widget,gpointer user_data) {
+  GtkWidget *save_dialog;
+  GtkFileChooser *chooser;
+  GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
+  gchar *filename;
+  gint res;
+  struct desc *cmd;
+
+  save_dialog = gtk_file_chooser_dialog_new ("Save File",
+                                      GTK_WINDOW(dialog),
+                                      action,
+                                      "_Cancel",
+                                      GTK_RESPONSE_CANCEL,
+                                      "_Save",
+                                      GTK_RESPONSE_ACCEPT,
+                                      NULL);
+  chooser = GTK_FILE_CHOOSER (save_dialog);
+  gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
+  gtk_file_chooser_set_current_name(chooser,"midi.midi");
+  res = gtk_dialog_run (GTK_DIALOG (save_dialog));
+  if(res==GTK_RESPONSE_ACCEPT) {
+    char *savefilename=gtk_file_chooser_get_filename(chooser);
+    clearProperties();
+    midi_save_state();
+    saveProperties(savefilename);
+    g_free(savefilename);
+  }
+  gtk_widget_destroy(save_dialog);
+}
+
+static void load_cb(GtkWidget *widget,gpointer user_data) {
+  GtkWidget *load_dialog;
+  GtkFileChooser *chooser;
+  GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
+  gchar *filename;
+  gint res;
+  struct desc *cmd;
+
+  load_dialog = gtk_file_chooser_dialog_new ("Open MIDI File",
+                                      GTK_WINDOW(dialog),
+                                      action,
+                                      "_Cancel",
+                                      GTK_RESPONSE_CANCEL,
+                                      "_Load",
+                                      GTK_RESPONSE_ACCEPT,
+                                      NULL);
+  chooser = GTK_FILE_CHOOSER (load_dialog);
+  res = gtk_dialog_run (GTK_DIALOG (load_dialog));
+  if(res==GTK_RESPONSE_ACCEPT) {
+    char *loadfilename=gtk_file_chooser_get_filename(chooser);
+    clear_cb(NULL,NULL);
+    clearProperties();
+    loadProperties(loadfilename);
+    midi_restore_state();
+    g_signal_handler_block(G_OBJECT(selection), selection_signal_id);
+    load_store();
+    g_signal_handler_unblock(G_OBJECT(selection), selection_signal_id);
+    g_free(loadfilename);
+  }
+  gtk_widget_destroy(load_dialog);
+}
+
+static void load_original_cb(GtkWidget *widget,gpointer user_data) {
+  GtkWidget *load_dialog;
+  GtkFileChooser *chooser;
+  GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
+  gchar *filename;
+  gint res;
+  struct desc *cmd;
+
+  load_dialog = gtk_file_chooser_dialog_new ("Open LEGACY MIDI File",
+                                      GTK_WINDOW(dialog),
+                                      action,
+                                      "_Cancel",
+                                      GTK_RESPONSE_CANCEL,
+                                      "_Load",
+                                      GTK_RESPONSE_ACCEPT,
+                                      NULL);
+  chooser = GTK_FILE_CHOOSER (load_dialog);
+  res = gtk_dialog_run (GTK_DIALOG (load_dialog));
+  if(res==GTK_RESPONSE_ACCEPT) {
+    char *loadfilename=gtk_file_chooser_get_filename(chooser);
+    clear_cb(NULL,NULL);
+    MIDIstartup(loadfilename);
+    g_free(loadfilename);
+    g_signal_handler_block(G_OBJECT(selection), selection_signal_id);
+    load_store();
+    g_signal_handler_unblock(G_OBJECT(selection), selection_signal_id);
+  }
+  gtk_widget_destroy(load_dialog);
+}
+
+static void add_store(int key,struct desc *cmd) {
+  char str_event[16];
+  char str_channel[16];
+  char str_note[16];
+  char str_type[32];
+  char str_action[32];
+
+  //g_print("%s: key=%d desc=%p\n",__FUNCTION__,key,cmd);
+  switch(cmd->event) {
+    case MIDI_EVENT_NONE:
+      strcpy(str_event,"NONE");
+      break;
+    case MIDI_EVENT_NOTE:
+      strcpy(str_event,"NOTE");
+      break;
+    case MIDI_EVENT_CTRL:
+      strcpy(str_event,"CTRL");
+      break;
+    case MIDI_EVENT_PITCH:
+      strcpy(str_event,"PITCH");
+      break;
+  }
+  if (cmd->channel >= 0) {
+    sprintf(str_channel,"%d",cmd->channel);
+  } else {
+    sprintf(str_channel,"%s","Any");
+  }
+  sprintf(str_note,"%d",key);
+  switch(cmd->type) {
+    case MIDI_TYPE_NONE:
+      strcpy(str_type,"NONE");
+      break;
+    case MIDI_TYPE_KEY:
+      strcpy(str_type,"KEY");
+      break;
+    case MIDI_TYPE_KNOB:
+      strcpy(str_type,"KNOB/SLIDER");
+      break;
+    case MIDI_TYPE_WHEEL:
+      strcpy(str_type,"WHEEL");
+      break;
+  }
+  // ATTENTION: this assumes ActionTable is sorted by action enum
+  strcpy(str_action,ActionTable[cmd->action].str);
+  
+  //g_print("%s: Event=%s Channel=%s Note=%s Type=%s Action=%s\n", __FUNCTION__, str_event, str_channel, str_note, str_type, str_action);
+  gtk_list_store_prepend(store,&iter);
+  gtk_list_store_set(store,&iter,
+      EVENT_COLUMN,str_event,
+      CHANNEL_COLUMN,str_channel,
+      NOTE_COLUMN,str_note,
+      TYPE_COLUMN,str_type,
+      ACTION_COLUMN,str_action,
+      -1);
+
+  if(scrolled_window!=NULL) {
+    GtkAdjustment *adjustment=gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW(scrolled_window));
+    //g_print("%s: adjustment=%f lower=%f upper=%f\n",__FUNCTION__,gtk_adjustment_get_value(adjustment),gtk_adjustment_get_lower(adjustment),gtk_adjustment_get_upper(adjustment));
+    if(gtk_adjustment_get_value(adjustment)!=0.0) {
+      gtk_adjustment_set_value(adjustment,0.0);
+    }
+  }
+}
+
+static void load_store() {
+  struct desc *cmd;
+  gtk_list_store_clear(store);
+  for(int i=127;i>=0;i--) {
+    cmd=MidiCommandsTable[i];
+    while(cmd!=NULL) {
+      add_store(i,cmd);
+      cmd=cmd->next;
+    }
+  }
+}
+
+static void add_cb(GtkButton *widget,gpointer user_data) {
+
+  gchar *str_type=gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(newType));
+  gchar *str_action=gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(newAction));
+;
+
+  gint i;
+  gint type;
+  gint action;
+  gint onoff;
+
+  if(str_type==NULL || str_action==NULL) {
+    return;
+  }
+
+  if(strcmp(str_type,"KEY")==0) {
+    type=MIDI_TYPE_KEY;
+  } else if(strcmp(str_type,"KNOB/SLIDER")==0) {
+    type=MIDI_TYPE_KNOB;
+  } else if(strcmp(str_type,"WHEEL")==0) {
+    type=MIDI_TYPE_WHEEL;
+  } else {
+    type=MIDI_TYPE_NONE;
+  }
+
+  action=MIDI_ACTION_NONE;
+  onoff=0;
+  i=0;
+  while(ActionTable[i].action!=MIDI_ACTION_LAST) {
+    if(strcmp(ActionTable[i].str,str_action)==0) {
+      action=ActionTable[i].action;
+      onoff=ActionTable[i].onoff;
+      break;
+    }
+    i++;
+  }
+
+  g_print("%s: type=%s (%d) action=%s (%d)\n",__FUNCTION__,str_type,type,str_action,action);
+
+  struct desc *desc;
+  desc = (struct desc *) malloc(sizeof(struct desc));
+  desc->next = NULL;
+  desc->action = action; // MIDIaction
+  desc->type = type; // MIDItype
+  desc->event = thisEvent; // MIDevent
+  desc->onoff = onoff;
+  desc->delay = thisDelay;
+  desc->vfl1  = thisVfl1;
+  desc->vfl2  = thisVfl2;
+  desc->fl1   = thisFl1;
+  desc->fl2   = thisFl2;
+  desc->lft1  = thisLft1;
+  desc->lft2  = thisLft2;
+  desc->rgt1  = thisRgt1;
+  desc->rgt2  = thisRgt2;
+  desc->fr1   = thisFr1;
+  desc->fr2   = thisFr2;
+  desc->vfr1  = thisVfr1;
+  desc->vfr2  = thisVfr2;
+  desc->channel  = thisChannel;
+
+  gint key=thisNote;
+  if(key<0) key=0;
+  if(key>127) key=0;
+
+
+  MidiAddCommand(key, desc);
+  add_store(key,desc);
+
+  gtk_widget_set_sensitive(add_b,FALSE);
+  gtk_widget_set_sensitive(update_b,TRUE);
+  gtk_widget_set_sensitive(delete_b,TRUE);
+
+}
+
+static void update_cb(GtkButton *widget,gpointer user_data) {
+  char str_event[16];
+  char str_channel[16];
+  char str_note[16];
+  int i;
+  int onoff;
+
+  if (current_cmd == NULL) {
+    g_print("%s: current_cmd is NULL!\n", __FUNCTION__);
+    return;
+  }
+
+  gchar *str_type=gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(newType));
+  gchar *str_action=gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(newAction));
+;
+  //g_print("%s: type=%s action=%s\n",__FUNCTION__,str_type,str_action);
+
+  if(strcmp(str_type,"KEY")==0) {
+    thisType=MIDI_TYPE_KEY;
+  } else if(strcmp(str_type,"KNOB/SLIDER")==0) {
+    thisType=MIDI_TYPE_KNOB;
+  } else if(strcmp(str_type,"WHEEL")==0) {
+    thisType=MIDI_TYPE_WHEEL;
+  } else {
+    thisType=MIDI_TYPE_NONE;
+  }
+
+  thisAction=MIDI_ACTION_NONE;
+  i=0;
+  while(ActionTable[i].action!=MIDI_ACTION_LAST) {
+    if(strcmp(ActionTable[i].str,str_action)==0) {
+      thisAction=ActionTable[i].action;
+      onoff=ActionTable[i].onoff;
+      break;
+    }
+    i++;
+  }
+
+  current_cmd->channel=thisChannel;
+  current_cmd->type   =thisType;
+  current_cmd->action =thisAction;
+  current_cmd->onoff =onoff;
+  current_cmd->delay =thisDelay;
+
+  current_cmd->vfl1  =thisVfl1;
+  current_cmd->vfl2  =thisVfl2;
+  current_cmd->fl1   =thisFl1;
+  current_cmd->fl2   =thisFl2;
+  current_cmd->lft1  =thisLft1;
+  current_cmd->lft2  =thisLft2;
+  current_cmd->rgt1  =thisRgt1;
+  current_cmd->rgt2  =thisRgt2;
+  current_cmd->fr1   =thisFr1;
+  current_cmd->fr2   =thisFr2;
+  current_cmd->vfr1  =thisVfr1;
+  current_cmd->vfr2  =thisVfr2;
+
+  switch(current_cmd->event) {
+    case MIDI_EVENT_NONE:
+      strcpy(str_event,"NONE");
+      break;
+    case MIDI_EVENT_NOTE:
+      strcpy(str_event,"NOTE");
+      break;
+    case MIDI_EVENT_CTRL:
+      strcpy(str_event,"CTRL");
+      break;
+    case MIDI_EVENT_PITCH:
+      strcpy(str_event,"PITCH");
+      break;
+  }
+  if (current_cmd->channel >= 0) {
+    sprintf(str_channel,"%d",current_cmd->channel);
+  } else {
+    sprintf(str_channel,"%s","Any");
+  }
+  sprintf(str_note,"%d",thisNote);
+
+  g_print("%s: event=%s channel=%s note=%s type=%s action=%s\n",
+          __FUNCTION__,str_event,str_channel,str_note,str_type,str_action);
+  gtk_list_store_set(store,&iter,
+      EVENT_COLUMN,str_event,
+      CHANNEL_COLUMN,str_channel,
+      NOTE_COLUMN,str_note,
+      TYPE_COLUMN,str_type,
+      ACTION_COLUMN,str_action,
+      -1);
+}
+
+static void delete_cb(GtkButton *widget,gpointer user_data) {
+  struct desc *previous_cmd;
+  struct desc *next_cmd;
+  GtkTreeIter saved_iter;
+  g_print("%s: thisNote=%d current_cmd=%p\n",__FUNCTION__,thisNote,current_cmd);
+
+  if (current_cmd == NULL) {
+    g_print("%s: current_cmd is NULL!\n", __FUNCTION__);
+    return;
+  }
+
+  saved_iter=iter;
+
+
+  // remove from MidiCommandsTable
+  if(MidiCommandsTable[thisNote]==current_cmd) {
+    g_print("%s: remove first\n",__FUNCTION__);
+    MidiCommandsTable[thisNote]=current_cmd->next;
+    g_free(current_cmd);
+    current_cmd=NULL;
+  } else {
+    previous_cmd=MidiCommandsTable[thisNote];
+    while(previous_cmd->next!=NULL) {
+      next_cmd=previous_cmd->next;
+      if(next_cmd==current_cmd) {
+        g_print("%s: remove next\n",__FUNCTION__);
+       previous_cmd->next=next_cmd->next;
+       g_free(next_cmd);
+        current_cmd=NULL;  // note next_cmd == current_cmd
+       break;
+      }
+      previous_cmd=next_cmd;
+    }
+  }
+
+  // remove from list store
+  gtk_list_store_remove(store,&saved_iter);
+
+  gtk_widget_set_sensitive(add_b,TRUE);
+  gtk_widget_set_sensitive(update_b,FALSE);
+  gtk_widget_set_sensitive(delete_b,FALSE);
+
+}
+
+void midi_menu(GtkWidget *parent) {
+  int i;
+  int col=0;
+  int row=0;
+  GtkCellRenderer *renderer;
+  GtkWidget *lbl;
+
+  parent_window=parent;
+
+  dialog=gtk_dialog_new();
+  gtk_window_set_transient_for(GTK_WINDOW(dialog),GTK_WINDOW(parent_window));
+  gtk_window_set_title(GTK_WINDOW(dialog),"piHPSDR - MIDI");
+  g_signal_connect (dialog, "delete_event", G_CALLBACK (delete_event), NULL);
+
+  GdkRGBA color;
+  color.red = 1.0;
+  color.green = 1.0;
+  color.blue = 1.0;
+  color.alpha = 1.0;
+  gtk_widget_override_background_color(dialog,GTK_STATE_FLAG_NORMAL,&color);
+
+  GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+
+  GtkWidget *grid=gtk_grid_new();
+  gtk_grid_set_column_spacing (GTK_GRID(grid),2);
+
+  row=0;
+  col=0;
+
+  GtkWidget *close_b=gtk_button_new_with_label("Close");
+  g_signal_connect(close_b, "pressed", G_CALLBACK(close_cb), NULL);
+  gtk_grid_attach(GTK_GRID(grid), close_b, col, row, 1, 1);
+  col++;
+
+  get_midi_devices();
+  if (n_midi_devices > 0) {
+    GtkWidget *devices_label=gtk_label_new(NULL);
+    gtk_label_set_markup(GTK_LABEL(devices_label), "<b>Select MIDI device(s)</b>");
+    gtk_label_set_justify(GTK_LABEL(devices_label),GTK_JUSTIFY_LEFT);
+    gtk_grid_attach(GTK_GRID(grid),devices_label,col,row,2,1);
+    //
+    // Now put the device checkboxes in columns 3 (width: 1), 4 (width: 3), 7 (width: 1)
+    // and make as many rows as necessary
+    col=3;
+    int width = 1;
+    for (i=0; i<n_midi_devices; i++) {
+      device_b[i] = gtk_check_button_new_with_label(midi_devices[i].name);
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(device_b[i]), midi_devices[i].active);
+      gtk_grid_attach(GTK_GRID(grid),device_b[i],col,row,width,1);
+      switch (col) {
+       case 3:
+         col=4;
+          width=3;
+         break;
+        case 4:
+         col=7;
+          width=1;
+          break;
+       case 7:
+         col=3;
+          width=1;
+          row++;
+         break;
+      }
+      g_signal_connect(device_b[i], "toggled", G_CALLBACK(device_cb), GINT_TO_POINTER(i));
+      gtk_widget_show(device_b[i]);
+    }
+    //
+    // Row containing device checkboxes is partially filled,
+    // advance to next one.
+    if (col > 3) {
+      col=0;
+      row++;
+    }
+  } else {
+    GtkWidget *devices_label=gtk_label_new(NULL);
+    gtk_label_set_markup(GTK_LABEL(devices_label), "<b>No MIDI devices found!</b>");
+    gtk_label_set_justify(GTK_LABEL(devices_label),GTK_JUSTIFY_LEFT);
+    gtk_grid_attach(GTK_GRID(grid),devices_label,col,row,3,1);
+    row++;
+    col=0;
+  }
+
+  row++;
+  col=0;
+
+  GtkWidget *clear_b=gtk_button_new_with_label("Clear");
+  gtk_grid_attach(GTK_GRID(grid),clear_b,col,row,1,1);
+  g_signal_connect(clear_b,"clicked",G_CALLBACK(clear_cb),NULL);
+  col++;
+
+  GtkWidget *save_b=gtk_button_new_with_label("Save");
+  gtk_grid_attach(GTK_GRID(grid),save_b,col,row,1,1);
+  g_signal_connect(save_b,"clicked",G_CALLBACK(save_cb),NULL);
+  col++;
+
+  GtkWidget *load_b=gtk_button_new_with_label("Load");
+  gtk_grid_attach(GTK_GRID(grid),load_b,col,row,1,1);
+  g_signal_connect(load_b,"clicked",G_CALLBACK(load_cb),NULL);
+  col++;
+
+  GtkWidget *load_original_b=gtk_button_new_with_label("Load Legacy");
+  gtk_grid_attach(GTK_GRID(grid),load_original_b,col,row,1,1);
+  g_signal_connect(load_original_b,"clicked",G_CALLBACK(load_original_cb),NULL);
+  col++;
+
+  configure_b=gtk_check_button_new_with_label("MIDI Configure");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (configure_b), FALSE);
+  gtk_grid_attach(GTK_GRID(grid),configure_b,col,row,3,1);
+  g_signal_connect(configure_b,"toggled",G_CALLBACK(configure_cb),NULL);
+
+  col+=3;
+  any_b=gtk_check_button_new_with_label("Configure for any channel");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (any_b), FALSE);
+  gtk_grid_attach(GTK_GRID(grid),any_b,col,row,6,1);
+  g_signal_connect(any_b,"toggled",G_CALLBACK(any_cb),NULL);
+
+  row++;
+  col=0;
+  GtkWidget *label=gtk_label_new("Evt");
+  gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+  label=gtk_label_new("Ch");
+  gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+  label=gtk_label_new("Note");
+  gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+  label=gtk_label_new("Type");
+  gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+  label=gtk_label_new("Value");
+  gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+  label=gtk_label_new("Min");
+  gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+  label=gtk_label_new("Max");
+  gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+  label=gtk_label_new("Action");
+  gtk_grid_attach(GTK_GRID(grid),label,col++,row,1,1);
+
+
+  row++;
+  col=0;
+  newEvent=gtk_label_new("");
+  gtk_grid_attach(GTK_GRID(grid),newEvent,col++,row,1,1);
+  newChannel=gtk_label_new("");
+  gtk_grid_attach(GTK_GRID(grid),newChannel,col++,row,1,1);
+  newNote=gtk_label_new("");
+  gtk_grid_attach(GTK_GRID(grid),newNote,col++,row,1,1);
+  newType=gtk_combo_box_text_new();
+  gtk_grid_attach(GTK_GRID(grid),newType,col++,row,1,1);
+  g_signal_connect(newType,"changed",G_CALLBACK(type_changed_cb),NULL);
+  newVal=gtk_label_new("");
+  gtk_grid_attach(GTK_GRID(grid),newVal,col++,row,1,1);
+  newMin=gtk_label_new("");
+  gtk_grid_attach(GTK_GRID(grid),newMin,col++,row,1,1);
+  newMax=gtk_label_new("");
+  gtk_grid_attach(GTK_GRID(grid),newMax,col++,row,1,1);
+  newAction=gtk_combo_box_text_new();
+  gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(newAction),4);
+  gtk_grid_attach(GTK_GRID(grid),newAction,col++,row,3,1);
+
+//
+// Load Action button with all actions, such that it
+// *now* assumes the maximum width
+//
+   i=0;
+   while(ActionTable[i].action!=MIDI_ACTION_LAST) {
+     gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newAction),NULL,ActionTable[i].str);
+     i++;
+   }
+   gtk_combo_box_set_active(GTK_COMBO_BOX(newAction),0);
+
+  row++;
+  col=0;
+
+  add_b=gtk_button_new_with_label("Add");
+  g_signal_connect(add_b, "pressed", G_CALLBACK(add_cb),NULL);
+  gtk_grid_attach(GTK_GRID(grid),add_b,col++,row,1,1);
+  gtk_widget_set_sensitive(add_b,FALSE);
+
+  update_b=gtk_button_new_with_label("Update");
+  g_signal_connect(update_b, "pressed", G_CALLBACK(update_cb),NULL);
+  gtk_grid_attach(GTK_GRID(grid),update_b,col++,row,1,1);
+  gtk_widget_set_sensitive(update_b,FALSE);
+
+  delete_b=gtk_button_new_with_label("Delete");
+  g_signal_connect(delete_b, "pressed", G_CALLBACK(delete_cb),NULL);
+  gtk_grid_attach(GTK_GRID(grid),delete_b,col++,row,1,1);
+  gtk_widget_set_sensitive(delete_b,FALSE);
+
+  row++;
+  col=0;
+
+  scrolled_window=gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),GTK_POLICY_AUTOMATIC,GTK_POLICY_ALWAYS);
+  //
+  // At the top of the window, there are rows of checkboxes for MIDI devices, up to 3 in a row.
+  // In the unlikely case there are very many MIDI devices, vertical space becomes scarce
+  //
+  gtk_widget_set_size_request(scrolled_window,400,300-15*((n_midi_devices+1)/3));
+
+  view=gtk_tree_view_new();
+
+  renderer=gtk_cell_renderer_text_new();
+  gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Event", renderer, "text", EVENT_COLUMN, NULL);
+
+  renderer=gtk_cell_renderer_text_new();
+  gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "CHANNEL", renderer, "text", CHANNEL_COLUMN, NULL);
+
+  renderer=gtk_cell_renderer_text_new();
+  gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "NOTE", renderer, "text", NOTE_COLUMN, NULL);
+
+  renderer=gtk_cell_renderer_text_new();
+  gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "TYPE", renderer, "text", TYPE_COLUMN, NULL);
+
+  renderer=gtk_cell_renderer_text_new();
+  gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "ACTION", renderer, "text", ACTION_COLUMN, NULL);
+
+  store=gtk_list_store_new(N_COLUMNS,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING);
+
+  load_store();
+
+  gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
+
+  gtk_container_add(GTK_CONTAINER(scrolled_window),view);
+
+  gtk_grid_attach(GTK_GRID(grid), scrolled_window, col, row, 5, 10);
+
+  model=gtk_tree_view_get_model(GTK_TREE_VIEW(view));
+  g_signal_connect(model,"row-inserted",G_CALLBACK(row_inserted_cb),NULL);
+
+  selection=gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+  gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+  selection_signal_id=g_signal_connect(G_OBJECT(selection),"changed",G_CALLBACK(tree_selection_changed_cb),NULL);
+
+  //
+  // Place a fixed container to hold the wheel parameters
+  // and create sub-grid
+  //
+  col=5;
+  WheelContainer=gtk_fixed_new();
+  gtk_widget_set_size_request(WheelContainer,300,300-15*((n_midi_devices+1)/3));
+  gtk_grid_attach(GTK_GRID(grid), WheelContainer, col, row, 6, 10);
+  //
+  // Showing/hiding the container may resize-the columns of the main grid,
+  // and causing other elements to move around. Therefore create a further
+  // "dummy" frame that is always shown. The dummy must have the same width
+  // and a small height.
+  //
+  GtkWidget *DummyContainer=gtk_fixed_new();
+  gtk_widget_set_size_request(DummyContainer,300,1);
+  gtk_grid_attach(GTK_GRID(grid), DummyContainer, col, row, 6, 1);
+
+  GtkWidget *WheelGrid=gtk_grid_new();
+  gtk_grid_set_column_spacing (GTK_GRID(WheelGrid),2);
+
+  col=0;
+  row=0;
+
+  lbl=gtk_label_new(NULL);
+  // the new-line in the label get some space between the text and the spin buttons
+  gtk_label_set_markup(GTK_LABEL(lbl), "<b>Configure special WHEEL parameters\n</b>");
+  gtk_widget_set_size_request(lbl,300,30);
+  gtk_widget_set_halign(lbl, GTK_ALIGN_CENTER);
+
+  gtk_grid_attach(GTK_GRID(WheelGrid), lbl, col, row, 3, 1);
+
+  //
+  // Finally, put wheel config elements into the wheel grid
+  //
+  col=0;
+  row++;
+
+  lbl=gtk_label_new("Delay");
+  gtk_widget_set_halign(lbl, GTK_ALIGN_START);
+  gtk_grid_attach(GTK_GRID(WheelGrid), lbl, col, row, 1, 1);
+  col++;
+
+  set_delay = gtk_spin_button_new_with_range(0.0, 500.0, 10.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_delay, col, row, 1, 1);
+  g_signal_connect(set_delay, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(1));
+  col++;
+
+  row++;
+  col=0;
+  lbl=gtk_label_new("Left <<<");
+  gtk_widget_set_halign(lbl, GTK_ALIGN_START);
+  gtk_grid_attach(GTK_GRID(WheelGrid), lbl, col, row, 1, 1);
+  col++;
+
+  set_vfl1 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_vfl1, col, row, 1, 1);
+  g_signal_connect(set_vfl1, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(2));
+  col++;
+
+  set_vfl2 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_vfl2, col, row, 1, 1);
+  g_signal_connect(set_vfl2, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(3));
+  col++;
+
+  row++;
+  col=0;
+
+  lbl=gtk_label_new("Left <<");
+  gtk_widget_set_halign(lbl, GTK_ALIGN_START);
+  gtk_grid_attach(GTK_GRID(WheelGrid), lbl, col, row, 1, 1);
+  col++;
+  set_fl1 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_fl1, col, row, 1, 1);
+  g_signal_connect(set_fl1, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(4));
+  col++;
+
+  set_fl2 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_fl2, col, row, 1, 1);
+  g_signal_connect(set_fl2, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(5));
+  col++;
+
+  row++;
+  col=0;
+  lbl=gtk_label_new("Left <");
+  gtk_widget_set_halign(lbl, GTK_ALIGN_START);
+  gtk_grid_attach(GTK_GRID(WheelGrid), lbl, col, row, 1, 1);
+  col++;
+
+  set_lft1 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_lft1, col, row, 1, 1);
+  g_signal_connect(set_lft1, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(6));
+  col++;
+
+  set_lft2 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_lft2, col, row, 1, 1);
+  g_signal_connect(set_lft2, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(7));
+  col++;
+
+  row++;
+  col=0;
+  lbl=gtk_label_new("Right >");
+  gtk_widget_set_halign(lbl, GTK_ALIGN_START);
+  gtk_grid_attach(GTK_GRID(WheelGrid), lbl, col, row, 1, 1);
+  col++;
+
+  set_rgt1 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_rgt1, col, row, 1, 1);
+  g_signal_connect(set_rgt1, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(8));
+  col++;
+
+  set_rgt2 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_rgt2, col, row, 1, 1);
+  g_signal_connect(set_rgt2, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(9));
+  col++;
+
+  row++;
+  col=0;
+  lbl=gtk_label_new("Right >>");
+  gtk_widget_set_halign(lbl, GTK_ALIGN_START);
+  gtk_grid_attach(GTK_GRID(WheelGrid), lbl, col, row, 1, 1);
+  col++;
+
+  set_fr1 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_fr1, col, row, 1, 1);
+  g_signal_connect(set_fr1, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(10));
+  col++;
+
+  set_fr2 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_fr2, col, row, 1, 1);
+  g_signal_connect(set_fr2, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(11));
+  col++;
+
+  row++;
+  col=0;
+  lbl=gtk_label_new("Right >>>");
+  gtk_widget_set_halign(lbl, GTK_ALIGN_START);
+  gtk_grid_attach(GTK_GRID(WheelGrid), lbl, col, row, 1, 1);
+  col++;
+
+  set_vfr1 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_vfr1, col, row, 1, 1);
+  g_signal_connect(set_vfr1, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(12));
+  col++;
+
+  set_vfr2 = gtk_spin_button_new_with_range(-1.0, 127.0, 1.0);
+  gtk_grid_attach(GTK_GRID(WheelGrid), set_vfr2, col, row, 1, 1);
+  g_signal_connect(set_vfr2, "value-changed", G_CALLBACK(wheelparam_cb), GINT_TO_POINTER(13));
+  col++;
+
+  gtk_container_add(GTK_CONTAINER(content),grid);
+  gtk_container_add(GTK_CONTAINER(WheelContainer), WheelGrid);
+  sub_menu=dialog;
+  gtk_widget_show_all(dialog);
+
+  //
+  // Clear Action box (we filled it just to set its width)
+  //
+  gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(newAction));
+  gtk_combo_box_set_active (GTK_COMBO_BOX(newAction),0);
+  //
+  // Hide "accept from any source" checkbox
+  // (made visible only if config is checked)
+  gtk_widget_hide(any_b);
+  gtk_widget_hide(WheelContainer);
+}
+
+static int update(void *data) {
+  int state=GPOINTER_TO_INT(data);
+  gchar text[32];
+  gint i=1;
+  gint j;
+
+  switch(state) {
+    case UPDATE_NEW:
+      g_print("%s: UPDATE_NEW\n",__FUNCTION__);
+      switch(thisEvent) {
+        case MIDI_EVENT_NONE:
+          gtk_label_set_text(GTK_LABEL(newEvent),"NONE");
+          break;
+        case MIDI_EVENT_NOTE:
+          gtk_label_set_text(GTK_LABEL(newEvent),"NOTE");
+          break;
+        case MIDI_EVENT_CTRL:
+          gtk_label_set_text(GTK_LABEL(newEvent),"CTRL");
+          break;
+        case MIDI_EVENT_PITCH:
+          gtk_label_set_text(GTK_LABEL(newEvent),"PITCH");
+          break;
+      }
+      if (thisChannel >= 0) {
+        sprintf(text,"%d",thisChannel);
+      } else {
+        strcpy(text,"Any");
+      }
+      gtk_label_set_text(GTK_LABEL(newChannel),text);
+      sprintf(text,"%d",thisNote);
+      gtk_label_set_text(GTK_LABEL(newNote),text);
+      gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(newType));
+      gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"NONE");
+      switch(thisEvent) {
+        case MIDI_EVENT_NONE:
+          gtk_combo_box_set_active (GTK_COMBO_BOX(newType),0);
+          break;
+        case MIDI_EVENT_NOTE:
+        case MIDI_EVENT_PITCH:
+          gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"KEY");
+          gtk_combo_box_set_active (GTK_COMBO_BOX(newType),1);
+          break;
+        case MIDI_EVENT_CTRL:
+          gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"KNOB/SLIDER");
+          gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"WHEEL");
+          gtk_combo_box_set_active (GTK_COMBO_BOX(newType),0);
+          break;
+      }
+      gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(newAction));
+      if(thisEvent==MIDI_EVENT_PITCH || thisEvent==MIDI_EVENT_NOTE) {
+       i=0;
+       j=0;
+       while(ActionTable[i].action!=MIDI_ACTION_LAST) {
+          if(ActionTable[i].type&MIDI_TYPE_KEY) {
+            gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newAction),NULL,ActionTable[i].str);
+            if(ActionTable[i].action==thisAction) {
+              gtk_combo_box_set_active(GTK_COMBO_BOX(newAction),j);
+            }
+            j++;
+          }
+          i++;
+        }
+
+      }
+      sprintf(text,"%d",thisVal);
+      gtk_label_set_text(GTK_LABEL(newVal),text);
+      sprintf(text,"%d",thisMin);
+      gtk_label_set_text(GTK_LABEL(newMin),text);
+      sprintf(text,"%d",thisMax);
+      gtk_label_set_text(GTK_LABEL(newMax),text);
+
+      gtk_widget_set_sensitive(add_b,TRUE);
+      gtk_widget_set_sensitive(update_b,FALSE);
+      gtk_widget_set_sensitive(delete_b,FALSE);
+      break;
+
+    case UPDATE_CURRENT:
+      g_print("%s: UPDATE_CURRENT\n",__FUNCTION__);
+      sprintf(text,"%d",thisVal);
+      gtk_label_set_text(GTK_LABEL(newVal),text);
+      sprintf(text,"%d",thisMin);
+      gtk_label_set_text(GTK_LABEL(newMin),text);
+      sprintf(text,"%d",thisMax);
+      gtk_label_set_text(GTK_LABEL(newMax),text);
+      break;
+
+    case UPDATE_EXISTING:
+      g_print("%s: UPDATE_EXISTING Type=%d Action=%d\n",__FUNCTION__,thisType,thisAction);
+      switch(thisEvent) {
+        case MIDI_EVENT_NONE:
+          gtk_label_set_text(GTK_LABEL(newEvent),"NONE");
+          break;
+        case MIDI_EVENT_NOTE:
+          gtk_label_set_text(GTK_LABEL(newEvent),"NOTE");
+          break;
+        case MIDI_EVENT_CTRL:
+          gtk_label_set_text(GTK_LABEL(newEvent),"CTRL");
+          break;
+        case MIDI_EVENT_PITCH:
+          gtk_label_set_text(GTK_LABEL(newEvent),"PITCH");
+          break;
+      }
+      if (thisChannel >= 0) {
+        sprintf(text,"%d",thisChannel);
+      } else {
+        sprintf(text,"%s","Any");
+      }
+      gtk_label_set_text(GTK_LABEL(newChannel),text);
+      sprintf(text,"%d",thisNote);
+      gtk_label_set_text(GTK_LABEL(newNote),text);
+      gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(newType));
+      gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"NONE");
+      switch(thisEvent) {
+        case MIDI_EVENT_NONE:
+         gtk_combo_box_set_active (GTK_COMBO_BOX(newType),0);
+          break;
+        case MIDI_EVENT_NOTE:
+        case MIDI_EVENT_PITCH:
+          gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"KEY");
+         if(thisType==MIDI_TYPE_NONE) {
+           gtk_combo_box_set_active (GTK_COMBO_BOX(newType),0);
+         } else if(thisType==MIDI_TYPE_KEY) {
+           gtk_combo_box_set_active (GTK_COMBO_BOX(newType),1);
+         }
+          break;
+        case MIDI_EVENT_CTRL:
+          gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"KNOB/SLIDER");
+          gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(newType),NULL,"WHEEL");
+         if(thisType==MIDI_TYPE_NONE) {
+           gtk_combo_box_set_active (GTK_COMBO_BOX(newType),0);
+         } else if(thisType==MIDI_TYPE_KNOB) {
+           gtk_combo_box_set_active (GTK_COMBO_BOX(newType),1);
+         } else if(thisType==MIDI_TYPE_WHEEL) {
+            gtk_combo_box_set_active (GTK_COMBO_BOX(newType),2);
+          }
+          break;
+      }
+      sprintf(text,"%d",thisVal);
+      gtk_label_set_text(GTK_LABEL(newVal),text);
+      sprintf(text,"%d",thisMin);
+      gtk_label_set_text(GTK_LABEL(newMin),text);
+      sprintf(text,"%d",thisMax);
+      gtk_label_set_text(GTK_LABEL(newMax),text);
+  
+      find_current_cmd();
+      g_print("%s: current_cmd %p\n",__FUNCTION__,current_cmd);
+
+      if (current_cmd != NULL) {
+        thisDelay = current_cmd->delay;
+        thisVfl1  = current_cmd->vfl1;
+        thisVfl2  = current_cmd->vfl2;
+        thisFl1   = current_cmd->fl1;
+        thisFl2   = current_cmd->fl2;
+        thisLft1  = current_cmd->lft1;
+        thisLft2  = current_cmd->lft2;
+        thisRgt1  = current_cmd->rgt1;
+        thisRgt2  = current_cmd->rgt2;
+        thisFr1   = current_cmd->fr1;
+        thisFr2   = current_cmd->fr2;
+        thisVfr1  = current_cmd->vfr1;
+        thisVfr2  = current_cmd->vfr2;
+      }
+      // no need for g_idle_add since this is called in the idle queue
+      update_wheelparams(NULL);
+      gtk_widget_set_sensitive(add_b,FALSE);
+      gtk_widget_set_sensitive(update_b,TRUE);
+      gtk_widget_set_sensitive(delete_b,TRUE);
+      break;
+
+  }
+
+  return 0;
+}
+
+void NewMidiConfigureEvent(enum MIDIevent event, int channel, int note, int val) {
+
+  gboolean valid;
+  char *str_event;
+  char *str_channel;
+  char *str_note;
+  char *str_type;
+  char *str_action;
+
+  gint tree_event;
+  gint tree_channel;
+  gint tree_note;
+
+  //g_print("%s: event=%d channel=%d note=%d val=%d\n", __FUNCTION__,event,channel,note,val);
+
+  if(event==thisEvent && channel==thisChannel && note==thisNote) {
+    //g_print("%s: current event\n",__FUNCTION__);
+    thisVal=val;
+    if(val<thisMin) thisMin=val;
+    if(val>thisMax) thisMax=val;
+    g_idle_add(update,GINT_TO_POINTER(UPDATE_CURRENT));
+  } else {
+    //g_print("%s: new or existing event\n",__FUNCTION__);
+    thisEvent=event;
+    thisChannel=channel;
+    if (accept_any) thisChannel=-1;
+    thisNote=note;
+    thisVal=val;
+    thisMin=val;
+    thisMax=val;
+    thisType=MIDI_TYPE_NONE;
+    thisAction=MIDI_ACTION_NONE;
+    //
+    // set default values for wheel parameters
+    //
+    thisDelay =  0;
+    thisVfl1  = -1;
+    thisVfl2  = -1;
+    thisFl1   = -1;
+    thisFl2   = -1;
+    thisLft1  =  0;
+    thisLft2  = 63;
+    thisRgt1  = 65;
+    thisRgt2  =127;
+    thisFr1   = -1;
+    thisFr2   = -1;
+    thisVfr1  = -1;
+    thisVfr2  = -1;
+
+    // search tree to see if it is existing event
+    valid=gtk_tree_model_get_iter_first(model,&iter);
+    while(valid) {
+      gtk_tree_model_get(model, &iter, EVENT_COLUMN, &str_event, -1);
+      gtk_tree_model_get(model, &iter, CHANNEL_COLUMN, &str_channel, -1);
+      gtk_tree_model_get(model, &iter, NOTE_COLUMN, &str_note, -1);
+      gtk_tree_model_get(model, &iter, TYPE_COLUMN, &str_type, -1);
+      gtk_tree_model_get(model, &iter, ACTION_COLUMN, &str_action, -1);
+
+      //g_print("%s: %s %s %s %s %s\n",__FUNCTION__,str_event,str_channel,str_note,str_type,str_action);
+
+      if(str_event!=NULL && str_channel!=NULL && str_note!=NULL && str_type!=NULL && str_action!=NULL) {
+        if(strcmp(str_event,"CTRL")==0) {
+          tree_event=MIDI_EVENT_CTRL;
+        } else if(strcmp(str_event,"PITCH")==0) {
+          tree_event=MIDI_EVENT_PITCH;
+        } else if(strcmp(str_event,"NOTE")==0) {
+          tree_event=MIDI_EVENT_NOTE;
+        } else {
+          tree_event=MIDI_EVENT_NONE;
+        }
+        if (!strncmp(str_channel,"Any", 3)) {
+         tree_channel=-1;
+        } else {
+          tree_channel=atoi(str_channel);
+        }
+        tree_note=atoi(str_note);
+
+       if(thisEvent==tree_event && thisChannel==tree_channel && thisNote==tree_note) {
+          thisVal=0;
+          thisMin=0;
+          thisMax=0;
+          if(strcmp(str_type,"KEY")==0) {
+            thisType=MIDI_TYPE_KEY;
+          } else if(strcmp(str_type,"KNOB/SLIDER")==0) {
+            thisType=MIDI_TYPE_KNOB;
+          } else if(strcmp(str_type,"WHEEL")==0) {
+            thisType=MIDI_TYPE_WHEEL;
+          } else {
+            thisType=MIDI_TYPE_NONE;
+          }
+          thisAction=MIDI_ACTION_NONE;
+          int i=1;
+          while(ActionTable[i].action!=MIDI_ACTION_LAST) {
+            if(strcmp(ActionTable[i].str,str_action)==0) {
+              thisAction=ActionTable[i].action;
+              break;
+            }
+            i++;
+          }
+         gtk_tree_view_set_cursor(GTK_TREE_VIEW(view),gtk_tree_model_get_path(model,&iter),NULL,FALSE);
+          g_idle_add(update,GINT_TO_POINTER(UPDATE_EXISTING));
+          return;
+       }
+      }
+
+      valid=gtk_tree_model_iter_next(model,&iter);
+    }
+    
+    //
+    // It is not guaranteed that update() will be executed before
+    // thisAction & friends are overwritten by the next incoming MIDI
+    // message. Therefore, we should allocate a data structure with
+    // all information therein that is needed by update() and pass
+    // a pointer.
+    //
+    g_idle_add(update,GINT_TO_POINTER(UPDATE_NEW));
+  }
+}
+
+void midi_save_state() {
+  char name[80];
+  char value[80];
+  struct desc *cmd;
+  gint index;
+
+  index=0;
+  for (int i=0; i<n_midi_devices; i++) {
+    if (midi_devices[i].active) {
+      sprintf(name,"mididevice[%d].name",index);
+      setProperty(name, midi_devices[i].name);
+      index++;
+    }
+  }
+
+    // the value i=128 is for the PitchBend
+    for(int i=0;i<129;i++) {
+      index=0;
+      cmd=MidiCommandsTable[i];
+      while(cmd!=NULL) {
+       //ATTENTION: g_print assumes ActionTable is sorted by action enum
+        //g_print("%s:  channel=%d key=%d event=%s type=%s action=%s\n",__FUNCTION__,cmd->channel,i,midi_events[cmd->event],midi_types[cmd->type],ActionTable[cmd->action].str);
+
+        //
+        // There might be events that share the channel and the note value (one NOTE and one CTRL, for example)
+        // These must not share the same key in the property database so the "running index" must be part of the key
+        //
+
+        sprintf(name,"midi[%d].index[%d].event",i,index);
+        setProperty(name,midi_events[cmd->event]);
+
+        sprintf(name,"midi[%d].index[%d].type",i,index);
+        setProperty(name,midi_types[cmd->type]);
+
+       //ATTENTION: assumes ActionTable is sorted by action enum
+        sprintf(name,"midi[%d].index[%d].action",i,index);
+        setProperty(name,(char *) ActionTable[cmd->action].str);
+
+        sprintf(name,"midi[%d].index[%d].channel",i,index);
+        sprintf(value,"%d",cmd->channel);
+        setProperty(name, value);
+
+        //
+        // For wheels, also store the additional parameters,
+        // but do so only if they deviate from the default values.
+        //
+        if (cmd->type == MIDI_TYPE_WHEEL) {
+          if (cmd->delay > 0) {
+            sprintf(name,"midi[%d].index[%d].delay",i,index);
+            sprintf(value,"%d",cmd->delay);
+            setProperty(name, value);
+          }
+          if (cmd->vfl1 != -1 || cmd->vfl2 != -1) {
+            sprintf(name,"midi[%d].index[%d].vfl1",i,index);
+            sprintf(value,"%d",cmd->vfl1);
+            setProperty(name, value);
+            sprintf(name,"midi[%d].index[%d].vfl2",i,index);
+            sprintf(value,"%d",cmd->vfl2);
+            setProperty(name, value);
+          }
+          if (cmd->fl1 != -1 || cmd->fl2 != -1) {
+            sprintf(name,"midi[%d].index[%d].fl1",i,index);
+            sprintf(value,"%d",cmd->fl1);
+            setProperty(name, value);
+            sprintf(name,"midi[%d].index[%d].fl2",i,index);
+            sprintf(value,"%d",cmd->fl2);
+            setProperty(name, value);
+          }
+          if (cmd->lft1 != 0  || cmd->lft2 != 63) {
+            sprintf(name,"midi[%d].index[%d].lft1",i,index);
+            sprintf(value,"%d",cmd->lft1);
+            setProperty(name, value);
+            sprintf(name,"midi[%d].index[%d].lft2",i,index);
+            sprintf(value,"%d",cmd->lft2);
+            setProperty(name, value);
+          }
+          if (cmd->rgt1 != 65 || cmd->rgt2 != 127) {
+            sprintf(name,"midi[%d].index[%d].rgt1",i,index);
+            sprintf(value,"%d",cmd->rgt1);
+            setProperty(name, value);
+            sprintf(name,"midi[%d].index[%d].rgt2",i,index);
+            sprintf(value,"%d",cmd->rgt2);
+            setProperty(name, value);
+          }
+          if (cmd->fr1 != -1 || cmd->fr2 != -1) {
+            sprintf(name,"midi[%d].index[%d].fr1",i,index);
+            sprintf(value,"%d",cmd->fr1);
+            setProperty(name, value);
+            sprintf(name,"midi[%d].index[%d].fr2",i,index);
+            sprintf(value,"%d",cmd->fr2);
+            setProperty(name, value);
+          }
+          if (cmd->vfr1 != -1 || cmd->vfr2 != -1) {
+            sprintf(name,"midi[%d].index[%d].vfr1",i,index);
+            sprintf(value,"%d",cmd->vfr1);
+            setProperty(name, value);
+            sprintf(name,"midi[%d].index[%d].vfr2",i,index);
+            sprintf(value,"%d",cmd->vfr2);
+            setProperty(name, value);
+          }
+        }
+
+        cmd=cmd->next;
+       index++;
+      }
+
+      if(index!=0) {
+        sprintf(name,"midi[%d].indices",i);
+        sprintf(value,"%d",index);
+        setProperty(name,value);
+      }
+
+    }
+}
+
+void midi_restore_state() {
+  char name[80];
+  char *value;
+  gint indices;
+  gint channel;
+  gint event;
+  gint onoff;
+  gint type;
+  gint action;
+  gint delay;
+  gint vfl1, vfl2;
+  gint fl1, fl2;
+  gint lft1, lft2;
+  gint rgt1, rgt2;
+  gint fr1, fr2;
+  gint vfr1, vfr2;
+
+  get_midi_devices();
+  MidiReleaseCommands();
+
+  //g_print("%s\n",__FUNCTION__);
+
+  //
+  // Note this is too early to open the MIDI devices, since the
+  // radio has not yet fully been configured. Therefore, only
+  // set the "active" flag, and the devices will be opened in
+  // radio.c when it is appropriate
+  //
+    
+  for(int i=0; i<MAX_MIDI_DEVICES; i++) {
+    sprintf(name,"mididevice[%d].name",i);
+    value=getProperty(name);
+    if (value) {
+      for (int j=0; j<n_midi_devices; j++) {
+        if(strcmp(midi_devices[j].name,value)==0) {
+          midi_devices[j].active=1;
+          g_print("%s: mark device %s at %d as active\n",__FUNCTION__,value,j);
+        }
+      }
+    }
+  }
+
+  // the value i=128 is for the PitchBend
+  for(int i=0;i<129;i++) {
+    sprintf(name,"midi[%d].indices",i);
+    value=getProperty(name);
+    if(value) {
+      indices=atoi(value);
+      for(int index=0; index<indices; index++) {
+        sprintf(name,"midi[%d].index[%d].event",i,index);
+        value=getProperty(name);
+       event=MIDI_EVENT_NONE;
+        if(value) {
+          for(int j=0;j<4;j++) {
+           if(strcmp(value,midi_events[j])==0) {
+             event=j;
+             break;
+            }
+          }
+       }
+        sprintf(name,"midi[%d].index[%d].type",i,index);
+        value=getProperty(name);
+       type=MIDI_TYPE_NONE;
+        if(value) {
+          for(int j=0;j<5;j++) {
+            if(strcmp(value,midi_types[j])==0) {
+              type=j;
+              break;
+            }
+          }
+       }
+        sprintf(name,"midi[%d].index[%d].action",i,index);
+        value=getProperty(name);
+       action=MIDI_ACTION_NONE;
+        if(value) {
+         int j=0;
+         while(ActionTable[j].type!=MIDI_ACTION_LAST) {
+            if(strcmp(value,ActionTable[j].str)==0) {
+              action=ActionTable[j].action;
+             break;
+            }
+           j++;
+         }
+       }
+        sprintf(name,"midi[%d].index[%d].channel",i,index);
+        value=getProperty(name);
+        channel=-1;
+        if (value) {
+          channel=atoi(value);
+          if (channel < -2 || channel > 15) channel=0;
+        }
+
+        sprintf(name,"midi[%d].index[%d].delay",i,index);
+        value=getProperty(name);
+        delay=0;
+        if (value) delay=atoi(value);
+
+        sprintf(name,"midi[%d].index[%d].vfl1",i,index);
+        value=getProperty(name);
+        vfl1=-1;
+        if (value) vfl1=atoi(value);
+        sprintf(name,"midi[%d].index[%d].vfl2",i,index);
+        value=getProperty(name);
+        vfl2=-1;
+        if (value) vfl2=atoi(value);
+
+        sprintf(name,"midi[%d].index[%d].fl1",i,index);
+        value=getProperty(name);
+        fl1=-1;
+        if (value) fl1=atoi(value);
+        sprintf(name,"midi[%d].index[%d].fl2",i,index);
+        value=getProperty(name);
+        fl2=-1;
+        if (value) fl2=atoi(value);
+
+        sprintf(name,"midi[%d].index[%d].lft1",i,index);
+        value=getProperty(name);
+        lft1=0;
+        if (value) lft1=atoi(value);
+        sprintf(name,"midi[%d].index[%d].lft2",i,index);
+        value=getProperty(name);
+        lft2=63;
+        if (value) lft2=atoi(value);
+
+        sprintf(name,"midi[%d].index[%d].rgt1",i,index);
+        value=getProperty(name);
+        rgt1=65;
+        if (value) rgt1=atoi(value);
+        sprintf(name,"midi[%d].index[%d].rgt2",i,index);
+        value=getProperty(name);
+        rgt2=127;
+        if (value) rgt2=atoi(value);
+
+        sprintf(name,"midi[%d].index[%d].fr1",i,index);
+        value=getProperty(name);
+        fr1=-1;
+        if (value) fr1=atoi(value);
+        sprintf(name,"midi[%d].index[%d].fr2",i,index);
+        value=getProperty(name);
+        fr2=-1;
+        if (value) fr2=atoi(value);
+
+        sprintf(name,"midi[%d].index[%d].vfr1",i,index);
+        value=getProperty(name);
+        vfr1=-1;
+        if (value) vfr1=atoi(value);
+        sprintf(name,"midi[%d].index[%d].vfr2",i,index);
+        value=getProperty(name);
+        vfr2=-1;
+        if (value) vfr2=atoi(value);
+
+       // ATTENTION: this assumes ActionTable is sorted by Action enums
+        onoff=ActionTable[action].onoff;
+
+       struct desc *desc = (struct desc *) malloc(sizeof(struct desc));
+
+        desc->next     = NULL;
+        desc->action   = action; // MIDIaction
+        desc->type     = type;   // MIDItype
+        desc->event    = event;  // MIDevent
+        desc->onoff    = onoff;
+        desc->delay    = delay;
+        desc->vfl1     = vfl1;
+        desc->vfl2     = vfl2;
+        desc->fl1      = fl1;
+        desc->fl2      = fl2;
+        desc->lft1     = lft1;
+        desc->lft2     = lft2;
+        desc->rgt1     = rgt1;
+        desc->rgt2     = rgt2;
+        desc->fr1      = fr1;
+        desc->fr2      = fr2;
+        desc->vfr1     = vfr1;
+        desc->vfr2     = vfr2;
+        desc->channel  = channel;
+
+//g_print("DESC INIT Note=%3d Action=%3d Type=%3d Event=%3d OnOff=%3d Chan=%3d Delay=%3d THR=%3d %3d %3d %3d %3d %3d %3d %3d %3d %3d %3d %3d\n",
+//        i, action, type, event, onoff, channel, delay,
+//        vfl1, vfl2, fl1, fl2, lft1, lft2, rgt1, rgt2, fr1, fr2, vfr1, vfr2);
+        MidiAddCommand(i, desc);
+      }
+    }
+  }
+}
+
diff --git a/midi_menu.h b/midi_menu.h
new file mode 100644 (file)
index 0000000..02220e1
--- /dev/null
@@ -0,0 +1,24 @@
+/* Copyright (C)
+* 2021 - John Melton, G0ORX/N6LYT
+*
+* 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*
+*/
+
+extern void midi_menu(GtkWidget *parent);
+extern void NewMidiConfigureEvent(enum MIDIevent event, int channel, int note, int val);
+extern void midi_save_state();
+extern void midi_restore_state();
+
index 9c7bf7ca3600190761d9308ed9c14ac67943e2e6..e1ed1e1cc196ece074b233c19103040a429487f6 100644 (file)
 #ifdef CLIENT_SERVER
 #include "server_menu.h"
 #endif
+#ifdef MIDI
+#include "midi.h"
+#include "midi_menu.h"
+#endif
 
 
 static GtkWidget *menu_b=NULL;
@@ -466,6 +470,18 @@ static gboolean server_cb (GtkWidget *widget, GdkEventButton *event, gpointer da
 }
 #endif
 
+#ifdef MIDI
+void start_midi() {
+  cleanup();
+  midi_menu(top_window);
+}
+
+static gboolean midi_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
+  start_midi();
+  return TRUE;
+}
+#endif
+
 void new_menu()
 {
   int i;
@@ -625,6 +641,13 @@ void new_menu()
     }
 #endif
 
+#ifdef MIDI
+    GtkWidget *midi_b=gtk_button_new_with_label("MIDI");
+    g_signal_connect (midi_b, "button-press-event", G_CALLBACK(midi_cb), NULL);
+    gtk_grid_attach(GTK_GRID(grid),midi_b,(i%5),i/5,1,1);
+    i++;
+#endif
+
 //
 //  We need at least two receivers and two ADCs to do DIVERSITY
 //
diff --git a/radio.c b/radio.c
index 2e7c64984cdb72a67e883fae218eae026e9c094c..b7ce5d0ff94d0ce31912613858ff116f46e6dcaf 100644 (file)
--- a/radio.c
+++ b/radio.c
 #endif
 #include "rigctl_menu.h"
 #ifdef MIDI
-// rather than including MIDI.h with all its internal stuff
-// (e.g. enum components) we just declare the single bit thereof
-// we need here to make a strict compiler happy.
-void MIDIstartup();
+#include "midi.h"
+#include "alsa_midi.h"
+#include "midi_menu.h"
 #endif
 #ifdef CLIENT_SERVER
 #include "client_server.h"
@@ -97,6 +96,8 @@ void MIDIstartup();
 #define TOOLBAR_HEIGHT (30)
 #define WATERFALL_HEIGHT (105)
 
+gint controller=NO_CONTROLLER;
+
 GtkWidget *fixed;
 static GtkWidget *vfo_panel;
 static GtkWidget *meter;
@@ -108,28 +109,30 @@ static GtkWidget *panadapter;
 static GtkWidget *waterfall;
 static GtkWidget *audio_waterfall;
 
+/*
 #ifdef GPIO
 static GtkWidget *encoders;
 static cairo_surface_t *encoders_surface = NULL;
 #endif
-       gint sat_mode;
+*/
+gint sat_mode;
 
-       int region=REGION_OTHER;
+int region=REGION_OTHER;
 
-       int echo=0;
+int echo=0;
 
-       int radio_sample_rate;
-       gboolean iqswap;
+int radio_sample_rate;
+gboolean iqswap;
 
-       static gint save_timer_id;
+static gint save_timer_id;
 
-       DISCOVERED *radio=NULL;
+DISCOVERED *radio=NULL;
 #ifdef CLIENT_SERVER
-       gboolean radio_is_remote=FALSE;
+gboolean radio_is_remote=FALSE;
 #endif
 
-       char property_path[128];
-        GMutex property_mutex;
+char property_path[128];
+GMutex property_mutex;
 
 RECEIVER *receiver[MAX_RECEIVERS];
 RECEIVER *active_receiver;
@@ -627,7 +630,7 @@ if(!radio_is_remote) {
       break;
 #ifdef SOAPYSDR
     case SOAPYSDR_PROTOCOL:
-      soapy_protocol_init(0,false);
+      soapy_protocol_init(FALSE);
       break;
 #endif
   }
@@ -1120,16 +1123,17 @@ void start_radio() {
 #ifdef SOAPYSDR
   adc[0].antenna=0;
   if(device==SOAPYSDR_USB_DEVICE) {
-    adc[0].rx_gain=malloc(radio->info.soapy.rx_gains*sizeof(gint));
-    for (size_t i = 0; i < radio->info.soapy.rx_gains; i++) {
-      adc[0].rx_gain[i]=0;
+    adc[0].gain=0;
+    if(radio->info.soapy.rx_gains>0) {
+      adc[0].min_gain=radio->info.soapy.rx_range[0].minimum;
+      adc[0].max_gain=radio->info.soapy.rx_range[0].maximum;;
+    } else {
+      adc[0].min_gain=0.0;
+      adc[0].max_gain=100.0;
     }
     adc[0].agc=FALSE;
     dac[0].antenna=1;
-    dac[0].tx_gain=malloc(radio->info.soapy.tx_gains*sizeof(gint));
-    for (size_t i = 0; i < radio->info.soapy.tx_gains; i++) {
-      dac[0].tx_gain[i]=0;
-    }
+    dac[0].gain=0;
   }
 #endif
 
@@ -1144,16 +1148,18 @@ void start_radio() {
 #ifdef SOAPYSDR
   adc[1].antenna=0;
   if(device==SOAPYSDR_USB_DEVICE) {
-    adc[1].rx_gain=malloc(radio->info.soapy.rx_gains*sizeof(gint));
-    for (size_t i = 0; i < radio->info.soapy.rx_gains; i++) {
-      adc[1].rx_gain[i]=0;
+    adc[1].gain=0;
+    if(radio->info.soapy.rx_gains>0) {
+      adc[1].min_gain=radio->info.soapy.rx_range[0].minimum;
+      adc[1].max_gain=radio->info.soapy.rx_range[0].maximum;;
+    } else {
+      adc[1].min_gain=0.0;
+      adc[1].max_gain=100.0;
     }
+    adc[1].max_gain=0;
     adc[1].agc=FALSE;
-
-    dac[1].tx_gain=malloc(radio->info.soapy.tx_gains*sizeof(gint));
-    for (size_t i = 0; i < radio->info.soapy.tx_gains; i++) {
-      dac[1].tx_gain[i]=0;
-    }
+    dac[1].antenna=1;
+    dac[1].gain=0;
   }
 
   radio_sample_rate=radio->info.soapy.sample_rate;
@@ -1258,28 +1264,15 @@ void start_radio() {
     if(can_transmit) {
       soapy_protocol_create_transmitter(transmitter);
       soapy_protocol_set_tx_antenna(transmitter,dac[0].antenna);
-/*
-      for(int i=0;i<radio->info.soapy.tx_gains;i++) {
-        soapy_protocol_set_tx_gain_element(transmitter,radio->info.soapy.tx_gain[i],dac[0].tx_gain[i]);
-      }
-*/
       soapy_protocol_set_tx_gain(transmitter,transmitter->drive);
       soapy_protocol_set_tx_frequency(transmitter);
       soapy_protocol_start_transmitter(transmitter);
     }
 
     soapy_protocol_set_rx_antenna(rx,adc[0].antenna);
-/*
-    for(int i=0;i<radio->info.soapy.rx_gains;i++) {
-      soapy_protocol_set_gain_element(rx,radio->info.soapy.rx_gain[i],adc[0].rx_gain[i]);
-    }
-*/
-
     soapy_protocol_set_rx_frequency(rx,VFO_A);
     soapy_protocol_set_automatic_gain(rx,adc[0].agc);
-    for(int i=0;i<radio->info.soapy.rx_gains;i++) {
-      soapy_protocol_set_gain_element(rx,radio->info.soapy.rx_gain[i],adc[0].rx_gain[i]);
-    }
+    soapy_protocol_set_gain(rx);
 
     if(vfo[0].ctun) {
       setFrequency(vfo[0].ctun_frequency);
@@ -1287,7 +1280,7 @@ void start_radio() {
     soapy_protocol_start_receiver(rx);
 
 //g_print("radio: set rf_gain=%f\n",rx->rf_gain);
-    soapy_protocol_set_gain(rx,rx->rf_gain);
+    soapy_protocol_set_gain(rx);
 
   }
 #endif
@@ -1296,13 +1289,25 @@ void start_radio() {
 
   gdk_window_set_cursor(gtk_widget_get_window(top_window),gdk_cursor_new(GDK_ARROW));
 
+#ifdef MIDI
   //
-  // MIDIstartup must not be called before the radio is completely set up, since
-  // then MIDI can asynchronously trigger actions which require the radio already
-  // running. So this is the last thing we do when starting the radio.
+  // The MIDI devices could not be opened in midi_restore_state() since MIDI events
+  // must not fly in before the radio is fully configured. Therefore midi_restore_state()
+  // simply marks the devices to be opened here by hi-jacking the "active" flag. Note that
+  // apart from this (ab)use, this flag is updated ONLY in register_midi_device() and
+  // close_midi_device().
   //
-#ifdef MIDI
-  MIDIstartup();
+  for (i=0; i<n_midi_devices; i++) {
+    if (midi_devices[i].active) {
+      //
+      // If device was marked "active" in the props file, open (register) it
+      // Note this flag is hi-jacked, so clear it before opening. It will be set
+      // if the MIDI device has been opened successfully
+      //
+      midi_devices[i].active=0;
+      register_midi_device(i);
+    }
+  }
 #endif
 
 #ifdef CLIENT_SERVER
@@ -1810,7 +1815,7 @@ void set_attenuation(int value) {
         break;
 #ifdef SOAPYSDR
       case SOAPYSDR_PROTOCOL:
-        soapy_protocol_set_gain(active_receiver,value * 1.0);
+        soapy_protocol_set_gain_element(active_receiver,radio->info.soapy.rx_gain[0],(int)adc[0].gain);
         break;
 #endif
     }
@@ -2080,9 +2085,7 @@ g_print("radioRestoreState: %s\n",property_path);
     memRestoreState();
     vfo_restore_state();
     modesettings_restore_state();
-#ifdef GPIO
     gpio_restore_actions();
-#endif
     value=getProperty("rigctl_enable");
     if(value) rigctl_enable=atoi(value);
     value=getProperty("rigctl_port_base");
@@ -2094,10 +2097,12 @@ g_print("radioRestoreState: %s\n",property_path);
     value=getProperty("rigctl_serial_port");
     if (value) strcpy(ser_port,value);
 
+    /*
     value=getProperty("adc_0_attenuation");
     if(value) adc_attenuation[0]=atoi(value);
     value=getProperty("adc_1_attenuation");
     if(value) adc_attenuation[1]=atoi(value);
+    */
        
     value=getProperty("split");
     if(value) split=atoi(value);
@@ -2108,27 +2113,85 @@ g_print("radioRestoreState: %s\n",property_path);
     value=getProperty("mute_rx_while_transmitting");
     if(value) mute_rx_while_transmitting=atoi(value);
 
-#ifdef SOAPYSDR
+    value=getProperty("radio.adc[0].filters");
+    if(value) adc[0].filters=atoi(value);
+    value=getProperty("radio.adc[0].hpf");
+    if(value) adc[0].hpf=atoi(value);
+    value=getProperty("radio.adc[0].lpf");
+    if(value) adc[0].lpf=atoi(value);
+    value=getProperty("radio.adc[0].antenna");
+    if(value) adc[0].antenna=atoi(value);
+    value=getProperty("radio.adc[0].dither");
+    if(value) adc[0].dither=atoi(value);
+    value=getProperty("radio.adc[0].random");
+    if(value) adc[0].random=atoi(value);
+    value=getProperty("radio.adc[0].preamp");
+    if(value) adc[0].preamp=atoi(value);
+    value=getProperty("radio.adc[0].attenuation");
+    if(value) adc[0].attenuation=atoi(value);
+    value=getProperty("radio.adc[0].enable_step_attenuation");
+    if(value) adc[0].enable_step_attenuation=atoi(value);
+    value=getProperty("radio.adc[0].gain");
+    if(value) adc[0].gain=atof(value);
+    value=getProperty("radio.adc[0].min_gain");
+    if(value) adc[0].min_gain=atof(value);
+    value=getProperty("radio.adc[0].max_gain");
+    if(value) adc[0].max_gain=atof(value);
+
+#ifdef  SOAPYSDR
     if(device==SOAPYSDR_USB_DEVICE) {
-      char name[128];
-      for(int i=0;i<radio->info.soapy.rx_gains;i++) {
-        sprintf(name,"radio.adc[0].rx_gain.%s",radio->info.soapy.rx_gain[i]) ;
-        value=getProperty(name);
-        if(value!=NULL) adc[0].rx_gain[i]=atoi(value);
-      }
       value=getProperty("radio.adc[0].agc");
-      if(value!=NULL) adc[0].agc=atoi(value);
-      value=getProperty("radio.adc[0].antenna");
-      if(value!=NULL) adc[0].antenna=atoi(value);
-  
-      value=getProperty("radio.dac[0].antenna");
-      if(value!=NULL) dac[0].antenna=atoi(value);
-      for(int i=0;i<radio->info.soapy.tx_gains;i++) {
-        sprintf(name,"radio.dac[0].tx_gain.%s",radio->info.soapy.tx_gain[i]);
-        value=getProperty(name);
-        if(value!=NULL) dac[0].tx_gain[i]=atoi(value);
+      if(value) adc[0].agc=atoi(value);
+    }
+#endif
+    value=getProperty("radio.dac[0].antenna");
+    if(value) dac[0].antenna=atoi(value);
+    value=getProperty("radio.dac[0].gain");
+    if(value) dac[0].gain=atof(value);
+
+    if(receivers>1) {
+      value=getProperty("radio.adc[1].filters");
+      if(value) adc[1].filters=atoi(value);
+      value=getProperty("radio.adc[1].hpf");
+      if(value) adc[1].hpf=atoi(value);
+      value=getProperty("radio.adc[1].lpf");
+      if(value) adc[1].lpf=atoi(value);
+      value=getProperty("radio.adc[1].antenna");
+      if(value) adc[1].antenna=atoi(value);
+      value=getProperty("radio.adc[1].dither");
+      if(value) adc[1].dither=atoi(value);
+      value=getProperty("radio.adc[1].random");
+      if(value) adc[1].random=atoi(value);
+      value=getProperty("radio.adc[1].preamp");
+      if(value) adc[1].preamp=atoi(value);
+      value=getProperty("radio.adc[1].attenuation");
+      if(value) adc[1].attenuation=atoi(value);
+      value=getProperty("radio.adc[1].enable_step_attenuation");
+      if(value) adc[1].enable_step_attenuation=atoi(value);
+      value=getProperty("radio.adc[1].gain");
+      if(value) adc[1].gain=atof(value);
+      value=getProperty("radio.adc[1].min_gain");
+      if(value) adc[1].min_gain=atof(value);
+      value=getProperty("radio.adc[1].max_gain");
+      if(value) adc[1].max_gain=atof(value);
+
+
+#ifdef  SOAPYSDR
+      if(device==SOAPYSDR_USB_DEVICE) {
+        value=getProperty("radio.adc[1].agc");
+        if(value) adc[1].agc=atoi(value);
       }
+#endif
+
+      value=getProperty("radio.dac[1].antenna");
+      if(value) dac[1].antenna=atoi(value);
+      value=getProperty("radio.dac[1].gain");
+      if(value) dac[1].gain=atof(value);
+
     }
+
+#ifdef MIDI
+    midi_restore_state();
 #endif
 
     value=getProperty("radio.display_sequence_errors");
@@ -2354,63 +2417,87 @@ g_print("radioSaveState: %s\n",property_path);
     sprintf(value,"%f",tone_level);
     setProperty("tone_level",value);
 
+    /*
     sprintf(value,"%d",adc_attenuation[0]);
     setProperty("adc_0_attenuation",value);
     sprintf(value,"%d",adc_attenuation[1]);
     setProperty("adc_1_attenuation",value);
+    */
        
     sprintf(value,"%d",rx_gain_calibration);
     setProperty("rx_gain_calibration",value);
 
-#ifdef SOAPYSDR
+    sprintf(value,"%d", adc[0].filters);
+    setProperty("radio.adc[0].filters",value);
+    sprintf(value,"%d", adc[0].hpf);
+    setProperty("radio.adc[0].hpf",value);
+    sprintf(value,"%d", adc[0].lpf);
+    setProperty("radio.adc[0].lpf",value);
+    sprintf(value,"%d", adc[0].antenna);
+    setProperty("radio.adc[0].antenna",value);
+    sprintf(value,"%d", adc[0].dither);
+    setProperty("radio.adc[0].dither",value);
+    sprintf(value,"%d", adc[0].random);
+    setProperty("radio.adc[0].random",value);
+    sprintf(value,"%d", adc[0].preamp);
+    setProperty("radio.adc[0].preamp",value);
+    sprintf(value,"%d", adc[0].attenuation);
+    setProperty("radio.adc[0].attenuation",value);
+    sprintf(value,"%d", adc[0].enable_step_attenuation);
+    setProperty("radio.adc[0].enable_step_attenuation",value);
+    sprintf(value,"%f", adc[0].gain);
+    setProperty("radio.adc[0].gain",value);
+    sprintf(value,"%f", adc[0].min_gain);
+    setProperty("radio.adc[0].min_gain",value);
+    sprintf(value,"%f", adc[0].max_gain);
+    setProperty("radio.adc[0].max_gain",value);
+
+
+#ifdef  SOAPYSDR
     if(device==SOAPYSDR_USB_DEVICE) {
-      char name[128];
-      for(int i=0;i<radio->info.soapy.rx_gains;i++) {
-        sprintf(name,"radio.adc[0].rx_gain.%s",radio->info.soapy.rx_gain[i]);
-        sprintf(value,"%d", adc[0].rx_gain[i]);
-        setProperty(name,value);
-      }
-      sprintf(name,"radio.adc[0].agc");
       sprintf(value,"%d", soapy_protocol_get_automatic_gain(receiver[0]));
-      setProperty(name,value);
-      sprintf(name,"radio.adc[0].antenna");
-      sprintf(value,"%d", adc[0].antenna);
-      setProperty(name,value);
-
-      sprintf(name,"radio.dac[0].antenna");
-      sprintf(value,"%d", dac[0].antenna);
-      setProperty(name,value);
-
-      for(int i=0;i<radio->info.soapy.tx_gains;i++) {
-        sprintf(name,"radio.dac[0].tx_gain.%s",radio->info.soapy.tx_gain[i]);
-        sprintf(value,"%d", dac[0].tx_gain[i]);
-        setProperty(name,value);
-      }
+      setProperty("radio.adc[0].agc",value);
+    }
+#endif
 
-      for(int i=0;i<radio->info.soapy.rx_gains;i++) {
-        sprintf(name,"radio.adc[1].rx_gain.%s",radio->info.soapy.rx_gain[i]);
-        sprintf(value,"%d", adc[1].rx_gain[i]);
-        setProperty(name,value);
-      }
-      sprintf(name,"radio.adc[1].agc");
-      sprintf(value,"%d", soapy_protocol_get_automatic_gain(receiver[1]));
-      setProperty(name,value);
-      sprintf(name,"radio.adc[1].antenna");
+   if(receivers>1) {
+      sprintf(value,"%d", adc[1].filters);
+      setProperty("radio.adc[1].filters",value);
+      sprintf(value,"%d", adc[1].hpf);
+      setProperty("radio.adc[1].hpf",value);
+      sprintf(value,"%d", adc[1].lpf);
+      setProperty("radio.adc[1].lpf",value);
       sprintf(value,"%d", adc[1].antenna);
-      setProperty(name,value);
-
-      sprintf(name,"radio.dac[1].antenna");
-      sprintf(value,"%d", dac[1].antenna);
-      setProperty(name,value);
-
-      for(int i=0;i<radio->info.soapy.tx_gains;i++) {
-        sprintf(name,"radio.dac[1].tx_gain.%s",radio->info.soapy.tx_gain[i]);
-        sprintf(value,"%d", dac[1].tx_gain[i]);
-        setProperty(name,value);
+      setProperty("radio.adc[1].antenna",value);
+      sprintf(value,"%d", adc[1].dither);
+      setProperty("radio.adc[1].dither",value);
+      sprintf(value,"%d", adc[1].random);
+      setProperty("radio.adc[1].random",value);
+      sprintf(value,"%d", adc[1].preamp);
+      setProperty("radio.adc[1].preamp",value);
+      sprintf(value,"%d", adc[1].attenuation);
+      setProperty("radio.adc[1].attenuation",value);
+      sprintf(value,"%d", adc[1].enable_step_attenuation);
+      setProperty("radio.adc[1].enable_step_attenuation",value);
+      sprintf(value,"%f", adc[1].gain);
+      setProperty("radio.adc[1].gain",value);
+      sprintf(value,"%f", adc[1].min_gain);
+      setProperty("radio.adc[1].min_gain",value);
+      sprintf(value,"%f", adc[1].max_gain);
+      setProperty("radio.adc[1].max_gain",value);
+
+#ifdef  SOAPYSDR
+      if(device==SOAPYSDR_USB_DEVICE) {
+        sprintf(value,"%d", soapy_protocol_get_automatic_gain(receiver[1]));
+        setProperty("radio.adc[1].agc",value);
       }
-    }
 #endif
 
+      sprintf(value,"%d", dac[1].antenna);
+      setProperty("radio.dac[1].antenna",value);
+      sprintf(value,"%f", dac[1].gain);
+      setProperty("radio.dac[1].gain",value);
+    }
 
     sprintf(value,"%d",receivers);
     setProperty("receivers",value);
@@ -2457,6 +2544,10 @@ g_print("radioSaveState: %s\n",property_path);
   }
 #endif
 
+#ifdef MIDI
+  midi_save_state();
+#endif
+
   saveProperties(property_path);
   g_mutex_unlock(&property_mutex);
 }
@@ -2564,9 +2655,6 @@ int remote_start(void *data) {
   reconfigure_radio();
   g_idle_add(ext_vfo_update,(gpointer)NULL);
   gdk_window_set_cursor(gtk_widget_get_window(top_window),gdk_cursor_new(GDK_ARROW));
-#ifdef MIDI
-  MIDIstartup();
-#endif
   for(int i=0;i<receivers;i++) {
     gint timer_id=gdk_threads_add_timeout_full(G_PRIORITY_DEFAULT_IDLE,100, start_spectrum, receiver[i], NULL);
   }
diff --git a/radio.h b/radio.h
index 564c9064892ae3601c7f63390ba8353729da0432..fb7726368e160976fc53eb17e3cfba629d0a423f 100644 (file)
--- a/radio.h
+++ b/radio.h
@@ -67,6 +67,8 @@ enum {
   PA_500W
 };
 
+extern gint controller;
+
 extern DISCOVERED *radio;
 #ifdef CLIENT_SERVER
 extern gboolean radio_is_remote;
@@ -105,7 +107,6 @@ extern RECEIVER *active_receiver;
 
 extern TRANSMITTER *transmitter;
 
-
 #define PA_DISABLED 0
 #define PA_ENABLED 1
 
index 3d4572448d9a3112a926c540e3637538a44df739..360261e24c5e5f1f3afb97c919f748008147a9e0 100644 (file)
@@ -81,37 +81,27 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d
 #ifdef SOAPYSDR
 static void rf_gain_value_changed_cb(GtkWidget *widget, gpointer data) {
   ADC *adc=(ADC *)data;
-  active_receiver->rf_gain=gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));
-  
-  if(radio->device==SOAPYSDR_USB_DEVICE) {
-    soapy_protocol_set_gain(receiver[0],active_receiver->rf_gain);
-  }
+  adc->gain=gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));
 
-/*
-  for(int i=0;i<radio->info.soapy.rx_gains;i++) {
-    int value=soapy_protocol_get_gain_element(active_receiver,radio->info.soapy.rx_gain[i]);
-    gtk_spin_button_set_value(GTK_SPIN_BUTTON(rx_gains[i]),(double)value);
+  if(radio->device==SOAPYSDR_USB_DEVICE) {
+    soapy_protocol_set_gain(receiver[0]);
   }
-*/
-
 }
 
 static void rx_gain_value_changed_cb(GtkWidget *widget, gpointer data) {
   ADC *adc=(ADC *)data;
-  int gain;
   if(radio->device==SOAPYSDR_USB_DEVICE) {
-    gain=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
-    soapy_protocol_set_gain_element(receiver[0],(char *)gtk_widget_get_name(widget),gain);
-
+    adc->gain=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+    soapy_protocol_set_gain_element(receiver[0],(char *)gtk_widget_get_name(widget),adc->gain);
 /*
     for(int i=0;i<radio->info.soapy.rx_gains;i++) {
       if(strcmp(radio->info.soapy.rx_gain[i],(char *)gtk_widget_get_name(widget))==0) {
         adc[0].rx_gain[i]=gain;
+        soapy_protocol_set_gain_element(receiver[0],(char *)gtk_widget_get_name(widget),gain);
         break;
       }
     }
 */
-
   }
 }
 
@@ -150,7 +140,10 @@ static void tx_gain_value_changed_cb(GtkWidget *widget, gpointer data) {
 static void agc_changed_cb(GtkWidget *widget, gpointer data) {
   ADC *adc=(ADC *)data;
   gboolean agc=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
-  soapy_protocol_set_automatic_gain(receiver[0],agc);
+  soapy_protocol_set_automatic_gain(active_receiver,agc);
+  if(!agc) {
+    soapy_protocol_set_gain(active_receiver);
+  }
 }
 
 /*
@@ -546,21 +539,44 @@ void radio_menu(GtkWidget *parent) {
   
 #ifdef SOAPYSDR
     case SOAPYSDR_PROTOCOL:
-      {
-      GtkWidget *sample_rate_label=gtk_label_new(NULL);
-      gtk_label_set_markup(GTK_LABEL(sample_rate_label), "<b>Sample Rate:</b>");
-      gtk_grid_attach(GTK_GRID(grid),sample_rate_label,col,row,1,1);
-      row++;
+      if(strcmp(radio->name,"sdrplay")==0) {
+        GtkWidget *sample_rate_combo_box=gtk_combo_box_text_new();
+//        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sample_rate_combo_box),NULL,"96000");
+//        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sample_rate_combo_box),NULL,"192000");
+//        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sample_rate_combo_box),NULL,"384000");
+        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sample_rate_combo_box),NULL,"768000");
+        switch(radio_sample_rate) {
+          case 96000:
+            gtk_combo_box_set_active(GTK_COMBO_BOX(sample_rate_combo_box),0);
+            break;
+          case 192000:
+            gtk_combo_box_set_active(GTK_COMBO_BOX(sample_rate_combo_box),1);
+            break;
+          case 384000:
+            gtk_combo_box_set_active(GTK_COMBO_BOX(sample_rate_combo_box),2);
+            break;
+          case 768000:
+            gtk_combo_box_set_active(GTK_COMBO_BOX(sample_rate_combo_box),3);
+            break;
+        }
+        g_signal_connect(sample_rate_combo_box,"changed",G_CALLBACK(sample_rate_cb),radio);
+        gtk_grid_attach(GTK_GRID(grid),sample_rate_combo_box,col,row,1,1);
+        row++;
+      } else {
+        GtkWidget *sample_rate_label=gtk_label_new(NULL);
+        gtk_label_set_markup(GTK_LABEL(sample_rate_label), "<b>Sample Rate:</b>");
+        gtk_grid_attach(GTK_GRID(grid),sample_rate_label,col,row,1,1);
+        row++;
 
-      char rate[16];
-      sprintf(rate,"%d",radio->info.soapy.sample_rate);
+        char rate[16];
+        sprintf(rate,"%d",radio->info.soapy.sample_rate);
 
-      GtkWidget *sample_rate=gtk_radio_button_new_with_label(NULL,rate);
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate), radio->info.soapy.sample_rate);
-      gtk_grid_attach(GTK_GRID(grid),sample_rate,col,row,1,1);
-      g_signal_connect(sample_rate,"toggled",G_CALLBACK(sample_rate_cb),GINT_TO_POINTER(radio->info.soapy.sample_rate));
+        GtkWidget *sample_rate=gtk_radio_button_new_with_label(NULL,rate);
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate), radio->info.soapy.sample_rate);
+        gtk_grid_attach(GTK_GRID(grid),sample_rate,col,row,1,1);
+        g_signal_connect(sample_rate,"toggled",G_CALLBACK(sample_rate_cb),GINT_TO_POINTER(radio->info.soapy.sample_rate));
 
-      col++;
+        col++;
       }
       break;
 #endif
index c60cd7cbfc581603016d6839f13df71cd487c0fb..12bacc5e19ccd6d678d9f676e909f472ba65e59f 100644 (file)
@@ -38,6 +38,7 @@
 #include "rx_panadapter.h"
 #include "vfo.h"
 #include "mode.h"
+#include "actions.h"
 #ifdef GPIO
 #include "gpio.h"
 #endif
@@ -365,6 +366,7 @@ void rx_panadapter_update(RECEIVER *rx) {
   }
 
   f = ((min_display/divisor)*divisor)+divisor;
+
   cairo_select_font_face(cr, DISPLAY_FONT,
                             CAIRO_FONT_SLANT_NORMAL,
                             CAIRO_FONT_WEIGHT_BOLD);
@@ -419,13 +421,13 @@ void rx_panadapter_update(RECEIVER *rx) {
 
   // agc
   if(rx->agc!=AGC_OFF) {
-    double knee_y=rx->agc_thresh+(double)adc_attenuation[rx->adc];
+    double knee_y=rx->agc_thresh+(double)adc[rx->adc].attenuation;
     if (filter_board == ALEX && rx->adc == 0) knee_y += (double)(10*rx->alex_attenuation);
     knee_y = floor((rx->panadapter_high - knee_y)
                         * (double) display_height
                         / (rx->panadapter_high - rx->panadapter_low));
 
-    double hang_y=rx->agc_hang+(double)adc_attenuation[rx->adc];
+    double hang_y=rx->agc_hang+(double)adc[rx->adc].attenuation;
     if (filter_board == ALEX && rx->adc == 0) hang_y += (double)(10*rx->alex_attenuation);
     hang_y = floor((rx->panadapter_high - hang_y)
                         * (double) display_height
@@ -487,9 +489,9 @@ void rx_panadapter_update(RECEIVER *rx) {
   samples[pan]=-200.0;
   samples[display_width-1+pan]=-200.0;
   if(have_rx_gain) {
-    s1=(double)samples[pan]+rx_gain_calibration-adc_attenuation[rx->adc];
+    s1=(double)samples[pan]+rx_gain_calibration-adc[rx->adc].attenuation;
   } else {
-    s1=(double)samples[pan]+(double)adc_attenuation[rx->adc];
+    s1=(double)samples[pan]+(double)adc[rx->adc].attenuation;
   }
   cairo_move_to(cr, 0.0, s1);
   if (filter_board == ALEX && rx->adc == 0) s1 += (double)(10*rx->alex_attenuation);
@@ -499,7 +501,8 @@ void rx_panadapter_update(RECEIVER *rx) {
   }
 #ifdef SOAPYSDR
   if(protocol==SOAPYSDR_PROTOCOL) {
-    s1-=rx->rf_gain;
+    //s1-=rx->rf_gain;
+    s1-=adc[rx->id].gain;
   }
 #endif
 
@@ -509,9 +512,9 @@ void rx_panadapter_update(RECEIVER *rx) {
   cairo_move_to(cr, 0.0, s1);
   for(i=1;i<display_width;i++) {
     if(have_rx_gain) {
-      s2=(double)samples[i+pan]+rx_gain_calibration-adc_attenuation[rx->adc];
+      s2=(double)samples[i+pan]+rx_gain_calibration-adc[rx->adc].attenuation;
     } else {
-      s2=(double)samples[i+pan]+(double)adc_attenuation[rx->adc];
+      s2=(double)samples[i+pan]+(double)adc[rx->adc].attenuation;
     }
     if (filter_board == ALEX && rx->adc == 0) s2 += (double)(10*rx->alex_attenuation);
     if (filter_board == CHARLY25) {
@@ -520,7 +523,8 @@ void rx_panadapter_update(RECEIVER *rx) {
     }
 #ifdef SOAPYSDR
     if(protocol==SOAPYSDR_PROTOCOL) {
-      s2-=rx->rf_gain;
+      //s2-=rx->rf_gain;
+      s2-=adc[rx->id].gain;
     }
 #endif
     s2 = floor((rx->panadapter_high - s2)
@@ -529,8 +533,7 @@ void rx_panadapter_update(RECEIVER *rx) {
     cairo_line_to(cr, (double)i, s2);
   }
 
-  cairo_pattern_t *gradient;
-  gradient=NULL;
+  cairo_pattern_t *gradient=NULL;
   if(display_gradient) {
     gradient = cairo_pattern_create_linear(0.0, display_height, 0.0, 0.0);
     // calculate where S9 is
@@ -576,12 +579,14 @@ void rx_panadapter_update(RECEIVER *rx) {
     //
     cairo_set_line_width(cr, 1.0);
   }
+  cairo_set_line_width(cr, LINE_WIDTH);
   cairo_stroke(cr);
 
   if(gradient) {
     cairo_pattern_destroy(gradient);
   }
 
+/*
 #ifdef GPIO
   if(rx->id==0 && controller==CONTROLLER1) {
 
@@ -606,7 +611,7 @@ void rx_panadapter_update(RECEIVER *rx) {
     }
   }
 #endif
-
+*/
   if(display_sequence_errors) {
     if(sequence_errors!=0) {
       cairo_move_to(cr,100.0,50.0);
index 76163eda07d63e390d1c7d9b5b61d165f573a7a1..7df32b50d0843a623f325fd9b42691c980975f3a 100644 (file)
--- a/sliders.c
+++ b/sliders.c
@@ -389,26 +389,33 @@ void set_af_gain(int rx,double value) {
 
 static void rf_gain_value_changed_cb(GtkWidget *widget, gpointer data) {
     active_receiver->rf_gain=gtk_range_get_value(GTK_RANGE(af_gain_scale));
+    switch(protocol) {
 #ifdef SOAPYSDR
-    if(protocol==SOAPYSDR_PROTOCOL) {
-      soapy_protocol_set_gain(active_receiver,active_receiver->rf_gain);
-    }
+      case SOAPYSDR_PROTOCOL:
+        soapy_protocol_set_gain(active_receiver);
+        break;
 #endif
+      default:
+        break;
+    }
 }
 
 void update_rf_gain() {
-  set_rf_gain(active_receiver->id,active_receiver->rf_gain);
+  //set_rf_gain(active_receiver->id,active_receiver->rf_gain);
+  set_rf_gain(active_receiver->id,adc[active_receiver->id].gain);
 }
 
 void set_rf_gain(int rx,double value) {
-  receiver[rx]->rf_gain=value;
+  g_print("%s\n",__FUNCTION__);
+  adc[receiver[rx]->id].gain=value;
 #ifdef SOAPYSDR
   if(protocol==SOAPYSDR_PROTOCOL) {
-    soapy_protocol_set_gain(active_receiver,active_receiver->rf_gain);
+    soapy_protocol_set_gain(receiver[rx]);
   }
 #endif
   if(display_sliders) {
-    gtk_range_set_value (GTK_RANGE(attenuation_scale),receiver[rx]->rf_gain);
+    //gtk_range_set_value (GTK_RANGE(attenuation_scale),receiver[rx]->rf_gain);
+    gtk_range_set_value (GTK_RANGE(rf_gain_scale),adc[receiver[rx]->id].gain);
   } else {
     if(scale_status!=RF_GAIN || scale_rx!=rx) {
       if(scale_status!=NO_FUNCTION) {
@@ -426,7 +433,8 @@ void set_rf_gain(int rx,double value) {
       GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(scale_dialog));
       rf_gain_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 100.0, 1.00);
       gtk_widget_set_size_request (rf_gain_scale, 400, 30);
-      gtk_range_set_value (GTK_RANGE(rf_gain_scale),receiver[rx]->rf_gain);
+      //gtk_range_set_value (GTK_RANGE(rf_gain_scale),receiver[rx]->rf_gain);
+      gtk_range_set_value (GTK_RANGE(rf_gain_scale),adc[receiver[rx]->id].gain);
       gtk_widget_show(rf_gain_scale);
       gtk_container_add(GTK_CONTAINER(content),rf_gain_scale);
       scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL);
@@ -434,7 +442,8 @@ void set_rf_gain(int rx,double value) {
       gtk_dialog_run(GTK_DIALOG(scale_dialog));
     } else {
       g_source_remove(scale_timer);
-      gtk_range_set_value (GTK_RANGE(rf_gain_scale),receiver[rx]->rf_gain);
+      //gtk_range_set_value (GTK_RANGE(rf_gain_scale),receiver[rx]->rf_gain);
+      gtk_range_set_value (GTK_RANGE(rf_gain_scale),adc[receiver[rx]->id].gain);
       scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL);
     }
   }
index c10550ad319edfd951311bfc2bdb63354e0b49c5..1c111ef2c627eb6214d6cda46aa16650b8bd8aa9 100644 (file)
@@ -26,6 +26,7 @@
 #include "SoapySDR/Device.h"
 #include "SoapySDR/Formats.h"
 #include "SoapySDR/Version.h"
+#include "SoapySDR/Logger.h"
 
 //#define TIMING
 #ifdef TIMING
 #include "filter.h"
 #include "receiver.h"
 #include "transmitter.h"
-//#include "wideband.h"
-//#include "adc.h"
-//#include "dac.h"
 #include "radio.h"
 #include "main.h"
-//#include "protocol1.h"
 #include "soapy_protocol.h"
 #include "audio.h"
 #include "signal.h"
 #include "ext.h"
 #include "error_handler.h"
 
-static double bandwidth=2000000.0;
 
-static SoapySDRDevice *soapy_device;
-static SoapySDRStream *rx_stream;
+#define MAX_CHANNELS 2
+static SoapySDRStream *rx_stream[MAX_CHANNELS];
 static SoapySDRStream *tx_stream;
+static SoapySDRDevice *soapy_device;
 static int max_samples;
 
 static int samples=0;
 
+static double bandwidth=2000000.0;
+
 static GThread *receive_thread_id;
 static gpointer receive_thread(gpointer data);
 
@@ -88,8 +87,16 @@ void soapy_protocol_set_mic_sample_rate(int rate) {
 }
 
 void soapy_protocol_change_sample_rate(RECEIVER *rx) {
+  int rc;
+
 // rx->mutex already locked
-  if(rx->sample_rate==radio_sample_rate) {
+  if(strcmp(radio->name,"sdrplay")==0) {
+    g_print("%s: setting samplerate=%f\n",__FUNCTION__,(double)rx->sample_rate);
+    rc=SoapySDRDevice_setSampleRate(soapy_device,SOAPY_SDR_RX,rx->adc,(double)rx->sample_rate);
+    if(rc!=0) {
+      g_print("%s: SoapySDRDevice_setSampleRate(%f) failed: %s\n",__FUNCTION__,(double)rx->sample_rate,SoapySDR_errToStr(rc));
+    }
+  } else if(rx->sample_rate==radio_sample_rate) {
     if(rx->resample_buffer!=NULL) {
       g_free(rx->resample_buffer);
       rx->resample_buffer=NULL;
@@ -121,41 +128,42 @@ void soapy_protocol_create_receiver(RECEIVER *rx) {
 
   mic_sample_divisor=rx->sample_rate/48000;
 
-fprintf(stderr,"soapy_protocol_create_receiver: setting samplerate=%f adc=%d mic_sample_divisor=%d\n",(double)radio_sample_rate,rx->adc,mic_sample_divisor);
+  g_print("%s: device=%p adc=%d setting bandwidth=%f\n",__FUNCTION__,soapy_device,rx->adc,bandwidth);
+  rc=SoapySDRDevice_setBandwidth(soapy_device,SOAPY_SDR_RX,rx->adc,bandwidth);
+  if(rc!=0) {
+    g_print("%s: SoapySDRDevice_setBandwidth(%f) failed: %s\n",__FUNCTION__,(double)bandwidth,SoapySDR_errToStr(rc));
+  }
+
+  g_print("%s: setting samplerate=%f device=%p adc=%d mic_sample_divisor=%d\n",__FUNCTION__,(double)radio_sample_rate,soapy_device,rx->adc,mic_sample_divisor);
   rc=SoapySDRDevice_setSampleRate(soapy_device,SOAPY_SDR_RX,rx->adc,(double)radio_sample_rate);
   if(rc!=0) {
-    fprintf(stderr,"soapy_protocol_create_receiver: SoapySDRDevice_setSampleRate(%f) failed: %s\n",(double)radio_sample_rate,SoapySDR_errToStr(rc));
+    g_print("%s: SoapySDRDevice_setSampleRate(%f) failed: %s\n",__FUNCTION__,(double)radio_sample_rate,SoapySDR_errToStr(rc));
   }
 
   size_t channel=rx->adc;
 #if defined(SOAPY_SDR_API_VERSION) && (SOAPY_SDR_API_VERSION < 0x00080000)
-fprintf(stderr,"soapy_protocol_create_receiver: SoapySDRDevice_setupStream(version<0x00080000): channel=%ld\n",channel);
-  rc=SoapySDRDevice_setupStream(soapy_device,&rx_stream,SOAPY_SDR_RX,SOAPY_SDR_CF32,&channel,1,NULL);
+  g_print("%s: SoapySDRDevice_setupStream(version<0x00080000): channel=%ld\n",__FUNCTION__,channel);
+  rc=SoapySDRDevice_setupStream(soapy_device,&rx_stream[channel],SOAPY_SDR_RX,SOAPY_SDR_CF32,&channel,1,NULL);
   if(rc!=0) {
-    fprintf(stderr,"soapy_protocol_create_receiver: SoapySDRDevice_setupStream (RX) failed: %s\n",SoapySDR_errToStr(rc));
+    g_print("%s: SoapySDRDevice_setupStream (RX) failed: %s\n",__FUNCTION__,SoapySDR_errToStr(rc));
     _exit(-1);
   }
 #else
-fprintf(stderr,"soapy_protocol_create_receiver: SoapySDRDevice_setupStream(version>=0x00080000): channel=%ld\n",channel);
-  rx_stream=SoapySDRDevice_setupStream(soapy_device,SOAPY_SDR_RX,SOAPY_SDR_CF32,&channel,1,NULL);
-  if(rx_stream==NULL) {
-    fprintf(stderr,"soapy_protocol_create_receiver: SoapySDRDevice_setupStream (RX) failed (rx_stream is NULL)\n");
+  g_print("%s: SoapySDRDevice_setupStream(version>=0x00080000): channel=%ld\n",__FUNCTION__,channel);
+  rx_stream[channel]=SoapySDRDevice_setupStream(soapy_device,SOAPY_SDR_RX,SOAPY_SDR_CF32,&channel,1,NULL);
+  if(rx_stream[channel]==NULL) {
+    g_print("%s: SoapySDRDevice_setupStream (RX) failed (rx_stream is NULL)\n",__FUNCTION__);
     _exit(-1);
   }
 #endif
 
+  g_print("%s: id=%d soapy_device=%p rx_stream=%p\n",__FUNCTION__,rx->id,soapy_device,rx_stream);
 
-  max_samples=SoapySDRDevice_getStreamMTU(soapy_device,rx_stream);
+  max_samples=SoapySDRDevice_getStreamMTU(soapy_device,rx_stream[channel]);
+  g_print("%s: max_samples=%d\n",__FUNCTION__,max_samples);
   if(max_samples>(2*rx->fft_size)) {
     max_samples=2*rx->fft_size;
   }
-  if(max_samples>=4096) {
-    max_samples=4096;
-  } else if(max_samples>=2048) {
-    max_samples=2048;
-  } else {
-    max_samples=1024;
-  }
   rx->buffer=g_new(double,max_samples*2);
 
   if(rx->sample_rate==radio_sample_rate) {
@@ -169,54 +177,58 @@ fprintf(stderr,"soapy_protocol_create_receiver: SoapySDRDevice_setupStream(versi
   }
 
 
-fprintf(stderr,"soapy_protocol_create_receiver: max_samples=%d buffer=%p\n",max_samples,rx->buffer);
+g_print("%s: max_samples=%d buffer=%p\n",__FUNCTION__,max_samples,rx->buffer);
 
 }
 
 void soapy_protocol_start_receiver(RECEIVER *rx) {
   int rc;
 
-double rate=SoapySDRDevice_getSampleRate(soapy_device,SOAPY_SDR_RX,rx->adc);
-fprintf(stderr,"soapy_protocol_start_receiver: activate_stream rate=%f\n",rate);
-  rc=SoapySDRDevice_activateStream(soapy_device, rx_stream, 0, 0LL, 0);
+  g_print("%s: id=%d soapy_device=%p rx_stream=%p\n",__FUNCTION__,rx->id,soapy_device,rx_stream);
+
+  size_t channel=rx->adc;
+  double rate=SoapySDRDevice_getSampleRate(soapy_device,SOAPY_SDR_RX,rx->adc);
+  g_print("%s: rate=%f\n",__FUNCTION__,rate);
+
+  g_print("%s: activate Stream\n",__FUNCTION__);
+  rc=SoapySDRDevice_activateStream(soapy_device, rx_stream[channel], 0, 0LL, 0);
   if(rc!=0) {
-    fprintf(stderr,"soapy_protocol_start_receiver: SoapySDRDevice_activateStream failed: %s\n",SoapySDR_errToStr(rc));
+    g_print("%s: SoapySDRDevice_activateStream failed: %s\n",__FUNCTION__,SoapySDR_errToStr(rc));
     _exit(-1);
   }
 
-fprintf(stderr,"soapy_protocol_start_receiver: create receive_thread\n");
+  g_print("%s: create receiver_thread\n",__FUNCTION__);
   receive_thread_id = g_thread_new( "soapy_rx", receive_thread, rx);
   if( ! receive_thread_id )
   {
-    fprintf(stderr,"g_thread_new failed for receive_thread\n");
+    g_print("%s: g_thread_new failed for receive_thread\n",__FUNCTION__);
     exit( -1 );
   }
-  fprintf(stderr, "receive_thread: id=%p\n",receive_thread_id);
+  g_print("%s: receiver_thread_id=%p\n",__FUNCTION__,receive_thread_id);
 }
 
 void soapy_protocol_create_transmitter(TRANSMITTER *tx) {
   int rc;
 
-
-fprintf(stderr,"soapy_protocol_create_transmitter: setting samplerate=%f\n",(double)tx->iq_output_rate);
+  g_print("%s: setting samplerate=%f\n",__FUNCTION__,(double)tx->iq_output_rate);
   rc=SoapySDRDevice_setSampleRate(soapy_device,SOAPY_SDR_TX,tx->dac,(double)tx->iq_output_rate);
   if(rc!=0) {
-    fprintf(stderr,"soapy_protocol_configure_transmitter: SoapySDRDevice_setSampleRate(%f) failed: %s\n",(double)tx->iq_output_rate,SoapySDR_errToStr(rc));
+    g_print("%s: SoapySDRDevice_setSampleRate(%f) failed: %s\n",__FUNCTION__,(double)tx->iq_output_rate,SoapySDR_errToStr(rc));
   }
 
 
   size_t channel=tx->dac;
-fprintf(stderr,"soapy_protocol_create_transmitter: SoapySDRDevice_setupStream: channel=%ld\n",channel);
+  g_print("%s: SoapySDRDevice_setupStream: channel=%ld\n",__FUNCTION__,channel);
 #if defined(SOAPY_SDR_API_VERSION) && (SOAPY_SDR_API_VERSION < 0x00080000)
   rc=SoapySDRDevice_setupStream(soapy_device,&tx_stream,SOAPY_SDR_TX,SOAPY_SDR_CF32,&channel,1,NULL);
   if(rc!=0) {
-    fprintf(stderr,"soapy_protocol_create_transmitter: SoapySDRDevice_setupStream (RX) failed: %s\n",SoapySDR_errToStr(rc));
+    g_print("%s: SoapySDRDevice_setupStream (RX) failed: %s\n",__FUNCTION__,SoapySDR_errToStr(rc));
     _exit(-1);
   }
 #else
   tx_stream=SoapySDRDevice_setupStream(soapy_device,SOAPY_SDR_TX,SOAPY_SDR_CF32,&channel,1,NULL);
   if(tx_stream==NULL) {
-    fprintf(stderr,"soapy_protocol_create_transmitter: SoapySDRDevice_setupStream (TX) failed: %s\n",SoapySDR_errToStr(rc));
+    g_print("%s: SoapySDRDevice_setupStream (TX) failed: %s\n",__FUNCTION__,SoapySDR_errToStr(rc));
     _exit(-1);
   }
 #endif
@@ -225,7 +237,7 @@ fprintf(stderr,"soapy_protocol_create_transmitter: SoapySDRDevice_setupStream: c
   if(max_tx_samples>(2*tx->fft_size)) {
     max_tx_samples=2*tx->fft_size;
   }
-fprintf(stderr,"soapy_protocol_create_transmitter: max_tx_samples=%d\n",max_tx_samples);
+  g_print("%s: max_tx_samples=%d\n",__FUNCTION__,max_tx_samples);
   output_buffer=(float *)malloc(max_tx_samples*sizeof(float)*2);
 
 }
@@ -253,38 +265,46 @@ fprintf(stderr,"soapy_protocol_stop_transmitter: deactivateStream\n");
   }
 }
 
-void soapy_protocol_init(int rx,gboolean hf) {
+void soapy_protocol_init(gboolean hf) {
   SoapySDRKwargs args={};
+  char temp[32];
   int rc;
   int i;
 
-fprintf(stderr,"soapy_protocol_init: rx=%d hf=%d\n",rx,hf);
+
+  SoapySDR_setLogLevel(SOAPY_SDR_TRACE);
+
+g_print("%s: hf=%d driver=%s\n",__FUNCTION__,hf,radio->name);
 
   // initialize the radio
-fprintf(stderr,"soapy_protocol_init: SoapySDRDevice_make\n");
   SoapySDRKwargs_set(&args, "driver", radio->name);
   if(strcmp(radio->name,"rtlsdr")==0) {
-    char id[16];
-    sprintf(id,"%d",radio->info.soapy.rtlsdr_count);
-    SoapySDRKwargs_set(&args, "rtl", id);
+    sprintf(temp,"%d",radio->info.soapy.rtlsdr_count);
+    SoapySDRKwargs_set(&args, "rtl", temp);
 
     if(hf) {
       SoapySDRKwargs_set(&args, "direct_samp", "2");
     } else {
       SoapySDRKwargs_set(&args, "direct_samp", "0");
     }
+  } else if(strcmp(radio->name,"sdrplay")==0) {
+    sprintf(temp,"SDRplay Dev%d",radio->info.soapy.sdrplay_count);
+    g_print("%s: label=%s\n",__FUNCTION__,temp);
+    SoapySDRKwargs_set(&args, "label", temp);
   }
   soapy_device=SoapySDRDevice_make(&args);
   if(soapy_device==NULL) {
-    fprintf(stderr,"soapy_protocol: SoapySDRDevice_make failed: %s\n",SoapySDRDevice_lastError());
+    g_print("%s: SoapySDRDevice_make failed: %s\n",__FUNCTION__,SoapySDRDevice_lastError());
     _exit(-1);
   }
   SoapySDRKwargs_clear(&args);
 
+  g_print("%s: soapy_device=%p\n",__FUNCTION__,soapy_device);
+
   if(can_transmit) {
     if(transmitter->local_microphone) {
       if(audio_open_input()!=0) {
-        fprintf(stderr,"audio_open_input failed\n");
+        g_print("%s: audio_open_input failed\n",__FUNCTION__);
         transmitter->local_microphone=0;
       }
     }
@@ -306,8 +326,9 @@ static void *receive_thread(void *arg) {
   float fsample;
   running=TRUE;
 fprintf(stderr,"soapy_protocol: receive_thread\n");
+  size_t channel=rx->adc;
   while(running) {
-    elements=SoapySDRDevice_readStream(soapy_device,rx_stream,buffs,max_samples,&flags,&timeNs,timeoutUs);
+    elements=SoapySDRDevice_readStream(soapy_device,rx_stream[channel],buffs,max_samples,&flags,&timeNs,timeoutUs);
     //fprintf(stderr,"soapy_protocol_receive_thread: SoapySDRDevice_readStream failed: max_samples=%d read=%d\n",max_samples,elements);
     if(elements<0) {
       continue;
@@ -367,11 +388,13 @@ fprintf(stderr,"soapy_protocol: receive_thread\n");
   }
 
 fprintf(stderr,"soapy_protocol: receive_thread: SoapySDRDevice_deactivateStream\n");
-  SoapySDRDevice_deactivateStream(soapy_device,rx_stream,0,0LL);
+  SoapySDRDevice_deactivateStream(soapy_device,rx_stream[channel],0,0LL);
+  /*
 fprintf(stderr,"soapy_protocol: receive_thread: SoapySDRDevice_closeStream\n");
-  SoapySDRDevice_closeStream(soapy_device,rx_stream);
+  SoapySDRDevice_closeStream(soapy_device,rx_stream[channel]);
 fprintf(stderr,"soapy_protocol: receive_thread: SoapySDRDevice_unmake\n");
   SoapySDRDevice_unmake(soapy_device);
+  */
   return NULL;
 }
 
@@ -466,10 +489,10 @@ void soapy_protocol_set_tx_antenna(TRANSMITTER *tx,int ant) {
   }
 }
 
-void soapy_protocol_set_gain(RECEIVER *rx,double gain) {
+void soapy_protocol_set_gain(RECEIVER *rx) {
   int rc;
 //fprintf(stderr,"soapy_protocol_set_gain: adc=%d gain=%f\n",gain);
-  rc=SoapySDRDevice_setGain(soapy_device,SOAPY_SDR_RX,rx->adc,gain);
+  rc=SoapySDRDevice_setGain(soapy_device,SOAPY_SDR_RX,rx->adc,adc[rx->adc].gain);
   if(rc!=0) {
     fprintf(stderr,"soapy_protocol: SoapySDRDevice_setGain failed: %s\n",SoapySDR_errToStr(rc));
   }
@@ -477,10 +500,10 @@ void soapy_protocol_set_gain(RECEIVER *rx,double gain) {
 
 void soapy_protocol_set_gain_element(RECEIVER *rx,char *name,int gain) {
   int rc;
-//fprintf(stderr,"soapy_protocol_set_gain: adc=%d %s=%d\n",rx->adc,name,gain);
+g_print("%s: adc=%d %s=%d\n",__FUNCTION__,rx->adc,name,gain);
   rc=SoapySDRDevice_setGainElement(soapy_device,SOAPY_SDR_RX,rx->adc,name,(double)gain);
   if(rc!=0) {
-    fprintf(stderr,"soapy_protocol: SoapySDRDevice_setGainElement %s failed: %s\n",name,SoapySDR_errToStr(rc));
+    g_print("%s: SoapySDRDevice_setGainElement %s failed: %s\n",__FUNCTION__,name,SoapySDR_errToStr(rc));
   }
 }
 
index 541dbf92974b1634a9d8af5e71c95c77cabc156b..c5bb4d63214c6658dd4d72f13833d0852f8f3d0b 100644 (file)
@@ -27,12 +27,12 @@ SoapySDRDevice *get_soapy_device();
 void soapy_protocol_create_receiver(RECEIVER *rx);
 void soapy_protocol_start_receiver(RECEIVER *rx);
 
-void soapy_protocol_init(int rx,gboolean hf);
+void soapy_protocol_init(gboolean hf);
 void soapy_protocol_stop();
 void soapy_protocol_set_rx_frequency(RECEIVER *rx,int v);
 void soapy_protocol_set_rx_antenna(RECEIVER *rx,int ant);
 void soapy_protocol_set_lna_gain(RECEIVER *rx,int gain);
-void soapy_protocol_set_gain(RECEIVER *rx,double gain);
+void soapy_protocol_set_gain(RECEIVER *rx);
 void soapy_protocol_set_gain_element(RECEIVER *rx,char *name,int gain);
 int soapy_protocol_get_gain_element(RECEIVER *rx,char *name);
 void soapy_protocol_change_sample_rate(RECEIVER *rx);
index 14f722e86be48aabecdd731e99dc9fd45c46f77d..dadb6ce5f37d9208b5062d10c1e4ee29ff3a4ed8 100644 (file)
@@ -18,6 +18,7 @@
 */
 
 #include <gtk/gtk.h>
+#include <glib/gprintf.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -31,6 +32,8 @@
 #include "receiver.h"
 #include "vfo.h"
 #include "button_text.h"
+#include "toolbar.h"
+#include "actions.h"
 #include "gpio.h"
 #include "i2c.h"
 
@@ -44,6 +47,8 @@ static GtkWidget *parent_window=NULL;
 
 static GtkWidget *dialog=NULL;
 
+static SWITCH *temp_switches;
+
 
 static void cleanup() {
   if(dialog!=NULL) {
@@ -64,17 +69,23 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d
   return FALSE;
 }
 
+void switch_page_cb(GtkNotebook *notebook,GtkWidget *page,guint page_num,gpointer user_data) {
+  g_print("%s: page %d\n",__FUNCTION__,page_num);
+  temp_switches=switches_controller1[page_num];
+}
 
-static void sw_select_cb(GtkWidget *widget, gpointer data) {
+static void switch_select_cb(GtkWidget *widget, gpointer data) {
   char text[128];
   CHOICE *choice=(CHOICE *)data;
-  sw_action[choice->sw]=choice->action;
+g_print("%s: temp_switches=%p\n",__FUNCTION__,temp_switches);
+  temp_switches[choice->sw].switch_function=choice->action;
   GtkWidget *label=gtk_bin_get_child(GTK_BIN(choice->button));
   sprintf(text,"<span size=\"smaller\">%s</span>",sw_string[choice->action]);
   gtk_label_set_markup (GTK_LABEL(label), text);
+  update_toolbar_labels();
 }
 
-static gboolean sw_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
+static gboolean switch_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
   int sw=GPOINTER_TO_INT(data);
   int i;
 
@@ -85,9 +96,8 @@ static gboolean sw_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
     choice->sw=sw;
     choice->action=i;
     choice->button=widget;
-    g_signal_connect(menu_item,"activate",G_CALLBACK(sw_select_cb),choice);
+    g_signal_connect(menu_item,"activate",G_CALLBACK(switch_select_cb),choice);
     gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
-fprintf(stderr,"%d=%s\n",i,sw_string[i]);
   }
   gtk_widget_show_all(menu);
 #if GTK_CHECK_VERSION(3,22,0)
@@ -100,293 +110,174 @@ fprintf(stderr,"%d=%s\n",i,sw_string[i]);
   return TRUE;
 }
 
+static void response_event(GtkWidget *dialog,gint id,gpointer user_data) {
+  g_print("%s: id=%d\n",__FUNCTION__,id);
+  if(id==GTK_RESPONSE_ACCEPT) {
+    g_print("%s: ACCEPT\n",__FUNCTION__);
+  }
+  gtk_widget_destroy(dialog);
+  dialog=NULL;
+  active_menu=NO_MENU;
+  sub_menu=NULL;
+}
+
 void switch_menu(GtkWidget *parent) {
-  int row=0;
-  int col=0;
-  char label[64];
-  int i;
+  gint row;
+  gint col;
+  gchar label[64];
+  GtkWidget *notebook;
+  GtkWidget *grid;
+  GtkWidget *widget;
+  gint function=0;
+
+g_print("%s: switches_no_controller=%p switches_controller1=%p switches_controller2_v1=%p switches_controller2_v2=%p\n",__FUNCTION__,&switches_no_controller,&switches_controller1,&switches_controller2_v1,&switches_controller2_v2);
 
-  dialog=gtk_dialog_new();
-  gtk_window_set_transient_for(GTK_WINDOW(dialog),GTK_WINDOW(parent_window));
-  //gtk_window_set_decorated(GTK_WINDOW(dialog),FALSE);
-  char title[32];
-  sprintf(title,"piHPSDR - Encoder Actions:");
-  gtk_window_set_title(GTK_WINDOW(dialog),title);
-  g_signal_connect (dialog, "delete_event", G_CALLBACK (delete_event), NULL);
+  dialog=gtk_dialog_new_with_buttons("piHPSDR - Switch Actions",GTK_WINDOW(parent),GTK_DIALOG_DESTROY_WITH_PARENT,("OK"),GTK_RESPONSE_ACCEPT,NULL);
+  g_signal_connect (dialog, "response", G_CALLBACK (response_event), NULL);
 
   GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
 
-  GtkWidget *grid=gtk_grid_new();
+  function=0;
+
+  if(controller==NO_CONTROLLER || controller==CONTROLLER1) {
+    notebook=gtk_notebook_new();
+  }
+next_function_set:
 
+  grid=gtk_grid_new();
   gtk_grid_set_column_homogeneous(GTK_GRID(grid),FALSE);
   gtk_grid_set_row_homogeneous(GTK_GRID(grid),FALSE);
   gtk_grid_set_column_spacing (GTK_GRID(grid),2);
   gtk_grid_set_row_spacing (GTK_GRID(grid),2);
 
-  GtkWidget *close_b=gtk_button_new_with_label("Close");
-  GtkWidget *close_label=gtk_bin_get_child(GTK_BIN(close_b));
-  sprintf(label,"<span size=\"smaller\">Close</span>");
-  gtk_label_set_markup (GTK_LABEL(close_label), label);
-  g_signal_connect (close_b, "pressed", G_CALLBACK(close_cb), NULL);
-  gtk_grid_attach(GTK_GRID(grid),close_b,col,row,1,1);
 
-  row++;
+  row=0;
   col=0;
 
+  gint max_switches=MAX_SWITCHES;
   switch(controller) {
-    default:
-      {
-      GtkWidget *sw7_title=gtk_label_new("SW7: ");
-      gtk_grid_attach(GTK_GRID(grid),sw7_title,col,row,1,1);
-      col++;
-
-      GtkWidget *sw7_combo_box=gtk_combo_box_text_new();
-      for(i=0;i<SWITCH_ACTIONS;i++) {
-        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sw7_combo_box),NULL,sw_string[i]);
-      }
-      gtk_combo_box_set_active(GTK_COMBO_BOX(sw7_combo_box),sw_action[6]);
-      g_signal_connect(sw7_combo_box,"changed",G_CALLBACK(sw_cb),GINT_TO_POINTER(CONTROLLER1_SW7));
-      gtk_grid_attach(GTK_GRID(grid),sw7_combo_box,col,row,1,1);
-      col++;
-
-      GtkWidget *sw1_title=gtk_label_new("SW1: ");
-      gtk_grid_attach(GTK_GRID(grid),sw1_title,col,row,1,1);
-      col++;
-
-      GtkWidget *sw1_combo_box=gtk_combo_box_text_new();
-      for(i=0;i<SWITCH_ACTIONS;i++) {
-        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sw1_combo_box),NULL,sw_string[i]);
-      }
-      gtk_combo_box_set_active(GTK_COMBO_BOX(sw1_combo_box),sw_action[0]);
-      g_signal_connect(sw1_combo_box,"changed",G_CALLBACK(sw_cb),GINT_TO_POINTER(CONTROLLER1_SW1));
-      gtk_grid_attach(GTK_GRID(grid),sw1_combo_box,col,row,1,1);
-      row++;
-      col=0;
-
-      GtkWidget *sw2_title=gtk_label_new("SW2: ");
-      gtk_grid_attach(GTK_GRID(grid),sw2_title,col,row,1,1);
-      col++;
-
-      GtkWidget *sw2_combo_box=gtk_combo_box_text_new();
-      for(i=0;i<SWITCH_ACTIONS;i++) {
-        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sw2_combo_box),NULL,sw_string[i]);
-      }
-      gtk_combo_box_set_active(GTK_COMBO_BOX(sw2_combo_box),sw_action[1]);
-      g_signal_connect(sw2_combo_box,"changed",G_CALLBACK(sw_cb),GINT_TO_POINTER(CONTROLLER1_SW2));
-      gtk_grid_attach(GTK_GRID(grid),sw2_combo_box,col,row,1,1);
-      col++;
-
-      GtkWidget *sw3_title=gtk_label_new("SW3: ");
-      gtk_grid_attach(GTK_GRID(grid),sw3_title,col,row,1,1);
-      col++;
-
-      GtkWidget *sw3_combo_box=gtk_combo_box_text_new();
-      for(i=0;i<SWITCH_ACTIONS;i++) {
-        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sw3_combo_box),NULL,sw_string[i]);
-      }
-      gtk_combo_box_set_active(GTK_COMBO_BOX(sw3_combo_box),sw_action[2]);
-      g_signal_connect(sw3_combo_box,"changed",G_CALLBACK(sw_cb),GINT_TO_POINTER(CONTROLLER1_SW3));
-      gtk_grid_attach(GTK_GRID(grid),sw3_combo_box,col,row,1,1);
-      row++;
-      col=0;
-
-      GtkWidget *sw4_title=gtk_label_new("SW4: ");
-      gtk_grid_attach(GTK_GRID(grid),sw4_title,col,row,1,1);
-      col++;
-
-      GtkWidget *sw4_combo_box=gtk_combo_box_text_new();
-      for(i=0;i<SWITCH_ACTIONS;i++) {
-        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sw4_combo_box),NULL,sw_string[i]);
-      }
-      gtk_combo_box_set_active(GTK_COMBO_BOX(sw4_combo_box),sw_action[3]);
-      g_signal_connect(sw4_combo_box,"changed",G_CALLBACK(sw_cb),GINT_TO_POINTER(CONTROLLER1_SW4));
-      gtk_grid_attach(GTK_GRID(grid),sw4_combo_box,col,row,1,1);
-      col++;
-
-      GtkWidget *sw5_title=gtk_label_new("SW5: ");
-      gtk_grid_attach(GTK_GRID(grid),sw5_title,col,row,1,1);
-      col++;
-
-      GtkWidget *sw5_combo_box=gtk_combo_box_text_new();
-      for(i=0;i<SWITCH_ACTIONS;i++) {
-        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sw5_combo_box),NULL,sw_string[i]);
-      }
-      gtk_combo_box_set_active(GTK_COMBO_BOX(sw5_combo_box),sw_action[4]);
-      g_signal_connect(sw5_combo_box,"changed",G_CALLBACK(sw_cb),GINT_TO_POINTER(CONTROLLER1_SW5));
-      gtk_grid_attach(GTK_GRID(grid),sw5_combo_box,col,row,1,1);
-      row++;
-      col=0;
-
-      GtkWidget *sw6_title=gtk_label_new("SW6: ");
-      gtk_grid_attach(GTK_GRID(grid),sw6_title,col,row,1,1);
-      col++;
-
-      GtkWidget *sw6_combo_box=gtk_combo_box_text_new();
-      for(i=0;i<SWITCH_ACTIONS;i++) {
-        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sw6_combo_box),NULL,sw_string[i]);
-      }
-      gtk_combo_box_set_active(GTK_COMBO_BOX(sw6_combo_box),sw_action[5]);
-      g_signal_connect(sw6_combo_box,"changed",G_CALLBACK(sw_cb),GINT_TO_POINTER(CONTROLLER1_SW6));
-      gtk_grid_attach(GTK_GRID(grid),sw6_combo_box,col,row,1,1);
-      col++;
-
-      GtkWidget *sw8_title=gtk_label_new("SW8: ");
-      gtk_grid_attach(GTK_GRID(grid),sw8_title,col,row,1,1);
-      col++;
-
-      GtkWidget *sw8_combo_box=gtk_combo_box_text_new();
-      for(i=0;i<SWITCH_ACTIONS;i++) {
-        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(sw8_combo_box),NULL,sw_string[i]);
-      }
-      gtk_combo_box_set_active(GTK_COMBO_BOX(sw8_combo_box),sw_action[7]);
-      g_signal_connect(sw8_combo_box,"changed",G_CALLBACK(sw_cb),GINT_TO_POINTER(CONTROLLER1_SW8));
-      gtk_grid_attach(GTK_GRID(grid),sw8_combo_box,col,row,1,1);
-      col++;
-      }
+    case NO_CONTROLLER:
+      max_switches=8;
+      temp_switches=switches_controller1[function];
+      break;
+    case CONTROLLER1:
+      max_switches=8;
+      temp_switches=switches_controller1[function];
       break;
-
     case CONTROLLER2_V1:
+      max_switches=16;
+      temp_switches=switches_controller2_v1;
+      break;
     case CONTROLLER2_V2:
-      {
-      col=8;
-
-      GtkWidget *sw13=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW13]]);
-      GtkWidget *sw13_label=gtk_bin_get_child(GTK_BIN(sw13));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW13]]);
-      gtk_label_set_markup (GTK_LABEL(sw13_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw13,col,row,1,1);
-      g_signal_connect (sw13, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW13));
-      row++;
-      col=7;
-
-      GtkWidget *sw12=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW12]]);
-      GtkWidget *sw12_label=gtk_bin_get_child(GTK_BIN(sw12));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW12]]);
-      gtk_label_set_markup (GTK_LABEL(sw12_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw12,col,row,1,1);
-      g_signal_connect (sw12, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW12));
-      col++;
-
-      GtkWidget *sw11=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW11]]);
-      GtkWidget *sw11_label=gtk_bin_get_child(GTK_BIN(sw11));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW11]]);
-      gtk_label_set_markup (GTK_LABEL(sw11_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw11,col,row,1,1);
-      g_signal_connect (sw11, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW11));
-      row++;
-      col=7;
-
-      GtkWidget *sw10=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW10]]);
-      GtkWidget *sw10_label=gtk_bin_get_child(GTK_BIN(sw10));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW10]]);
-      gtk_label_set_markup (GTK_LABEL(sw10_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw10,col,row,1,1);
-      g_signal_connect (sw10, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW10));
-      col++;
-
-      GtkWidget *sw9=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW9]]);
-      GtkWidget *sw9_label=gtk_bin_get_child(GTK_BIN(sw9));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW9]]);
-      gtk_label_set_markup (GTK_LABEL(sw9_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw9,col,row,1,1);
-      g_signal_connect (sw9, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW9));
-      row++;
-      col=7;
-
-      GtkWidget *sw7=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW7]]);
-      GtkWidget *sw7_label=gtk_bin_get_child(GTK_BIN(sw7));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW7]]);
-      gtk_label_set_markup (GTK_LABEL(sw7_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw7,col,row,1,1);
-      g_signal_connect (sw7, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW7));
-      col++;
-
-      GtkWidget *sw8=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW8]]);
-      GtkWidget *sw8_label=gtk_bin_get_child(GTK_BIN(sw8));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW8]]);
-      gtk_label_set_markup (GTK_LABEL(sw8_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw8,col,row,1,1);
-      g_signal_connect (sw8, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW8));
-      row++;
-      col=7;
-
-      GtkWidget *sw16=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW16]]);
-      GtkWidget *sw16_label=gtk_bin_get_child(GTK_BIN(sw16));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW16]]);
-      gtk_label_set_markup (GTK_LABEL(sw16_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw16,col,row,1,1);
-      g_signal_connect (sw16, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW16));
-      col++;
-
-      GtkWidget *sw17=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW17]]);
-      GtkWidget *sw17_label=gtk_bin_get_child(GTK_BIN(sw17));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW17]]);
-      gtk_label_set_markup (GTK_LABEL(sw17_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw17,col,row,1,1);
-      g_signal_connect (sw17, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW17));
-      row++;
-      col=0;
-
-      GtkWidget *sw2=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW2]]);
-      GtkWidget *sw2_label=gtk_bin_get_child(GTK_BIN(sw2));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW2]]);
-      gtk_label_set_markup (GTK_LABEL(sw2_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw2,col,row,1,1);
-      g_signal_connect (sw2, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW2));
-      col++;
-  
-      GtkWidget *sw3=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW3]]);
-      GtkWidget *sw3_label=gtk_bin_get_child(GTK_BIN(sw3));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW3]]);
-      gtk_label_set_markup (GTK_LABEL(sw3_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw3,col,row,1,1);
-      g_signal_connect (sw3, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW3));
-      col++;
-  
-      GtkWidget *sw4=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW4]]);
-      GtkWidget *sw4_label=gtk_bin_get_child(GTK_BIN(sw4));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW4]]);
-      gtk_label_set_markup (GTK_LABEL(sw4_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw4,col,row,1,1);
-      g_signal_connect (sw4, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW4));
-      col++;
-  
-      GtkWidget *sw5=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW5]]);
-      GtkWidget *sw5_label=gtk_bin_get_child(GTK_BIN(sw5));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW5]]);
-      gtk_label_set_markup (GTK_LABEL(sw5_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw5,col,row,1,1);
-      g_signal_connect (sw5, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW5));
-      col++;
-  
-      GtkWidget *sw6=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW6]]);
-      GtkWidget *sw6_label=gtk_bin_get_child(GTK_BIN(sw6));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW6]]);
-      gtk_label_set_markup (GTK_LABEL(sw6_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw6,col,row,1,1);
-      g_signal_connect (sw6, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW6));
-      col++;
-  
-      GtkWidget *sw14=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW14]]);
-      GtkWidget *sw14_label=gtk_bin_get_child(GTK_BIN(sw14));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW14]]);
-      gtk_label_set_markup (GTK_LABEL(sw14_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw14,col,row,1,1);
-      g_signal_connect (sw14, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW14));
-      col++;
-  
-      GtkWidget *sw15=gtk_button_new_with_label(sw_string[sw_action[CONTROLLER2_SW15]]);
-      GtkWidget *sw15_label=gtk_bin_get_child(GTK_BIN(sw15));
-      sprintf(label,"<span size=\"smaller\">%s</span>",sw_string[sw_action[CONTROLLER2_SW15]]);
-      gtk_label_set_markup (GTK_LABEL(sw15_label), label);
-      gtk_grid_attach(GTK_GRID(grid),sw15,col,row,1,1);
-      g_signal_connect (sw15, "button_press_event", G_CALLBACK(sw_cb), GINT_TO_POINTER(CONTROLLER2_SW15));
-      col++;
-      }
+      max_switches=16;
+      temp_switches=switches_controller2_v2;
       break;
   }
 
-  gtk_container_add(GTK_CONTAINER(content),grid);
+  g_print("%s: temp_switches=%p\n",__FUNCTION__,temp_switches);
+
+  int original_row=row;
+
+  if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
+    row=row+5;
+    col=0;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[0].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(0));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[1].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(1));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[2].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(2));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[3].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(3));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[4].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(4));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[5].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(5));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[6].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(6));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+
+    row=original_row;
+    col=8;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[7].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(7));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    row++;
+    col=7;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[8].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(8));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[9].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(9));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    row++;
+    col=7;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[10].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(10));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[11].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(11));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    row++;
+    col=7;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[12].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(12));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[13].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(13));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    row++;
+    col=7;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[14].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(14));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+    col++;
+    widget=gtk_button_new_with_label(sw_string[temp_switches[15].switch_function]);
+    g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(15));
+    gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+
+    gtk_container_add(GTK_CONTAINER(content),grid);
+  } else {
+    int start_row=row;
+    for(int i=0;i<max_switches;i++) {
+      if((controller==NO_CONTROLLER || controller==CONTROLLER1) && (temp_switches[i].switch_function==FUNCTION)) {
+        widget=gtk_label_new(NULL);
+        g_sprintf(label,"<b>%s</b>",sw_string[temp_switches[i].switch_function]);
+        gtk_label_set_markup (GTK_LABEL(widget), label);
+      } else {
+        widget=gtk_button_new_with_label(sw_string[temp_switches[i].switch_function]);
+        g_signal_connect(widget,"button_press_event",G_CALLBACK(switch_cb),GINT_TO_POINTER(i));
+      }
+      gtk_grid_attach(GTK_GRID(grid),widget,col,row,1,1);
+      col++;
+    }
+
+    g_sprintf(label,"Function %d",function);
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook),grid,gtk_label_new(label));
+    function++;
+    if(function<MAX_FUNCTIONS) {
+      goto next_function_set;
+    }
+    gtk_container_add(GTK_CONTAINER(content),notebook);
+    g_signal_connect (notebook, "switch-page",G_CALLBACK(switch_page_cb),NULL);
+  }
 
   sub_menu=dialog;
 
index 6727192a9e5c14ba8a8184c8a944ab880328f3db..246d89b3102da2d428d4ebf800b8a0070cf6a468 100644 (file)
--- a/toolbar.c
+++ b/toolbar.c
@@ -83,6 +83,8 @@ static gint rit_minus_timer=-1;
 static gint xit_plus_timer=-1;
 static gint xit_minus_timer=-1;
 
+SWITCH *toolbar_switches=switches_controller1[0];
+
 static gboolean rit_timer_cb(gpointer data) {
   int i=GPOINTER_TO_INT(data);
   vfo_rit(active_receiver->id,i);
index 5a1b8879783cb419a0b82ae981cf0e84af8281f6..ea9063a5b696cfac184b78060f8c7388ec774c03 100644 (file)
--- a/toolbar.h
+++ b/toolbar.h
 #ifndef _TOOLBAR_H
 #define _TOOLBAR_H
 
+#include "gpio.h"
 #define MAX_FUNCTION 5
 
 extern int function;
 
+extern SWITCH *toolbar_switches;
 
 void update_toolbar_labels();
 void ptt_update(int state);
index 015f46e839df65ddde03c9c47f72b7b69b536d47..ec7448370e2f8610ec852827ba4f335370b26a49 100644 (file)
@@ -670,6 +670,11 @@ static gboolean update_display(gpointer data) {
         // reflected power, so correct for that
         //
         double gamma=(double) rev_average / (double) fwd_average;
+        //
+        // this prevents SWR going to infinity, from which the
+        // moving average cannot recover
+        //
+        if (gamma > 0.95) gamma=0.95;
         tx->swr=0.7*(1+gamma)/(1-gamma) + 0.3*tx->swr;
     } else {
         //
index a7f30a80ca329fa29a0acdfa393b6a33ba88b4af..868b020c9a7b25dbe2af9f2ecba1d66cf01a4e98 100644 (file)
--- a/tx_menu.c
+++ b/tx_menu.c
@@ -384,9 +384,7 @@ void tx_menu(GtkWidget *parent) {
 
   GtkWidget *label=gtk_label_new(NULL);
   gtk_label_set_markup(GTK_LABEL(label), "<b>TX Filter: </b>");
-#ifdef GTK316
-  gtk_label_set_xalign(GTK_LABEL(label),0);
-#endif
+  gtk_widget_set_halign(label, GTK_ALIGN_START);
   gtk_grid_attach(GTK_GRID(grid),label,col,row,1,1);
 
   col++;
@@ -414,9 +412,7 @@ void tx_menu(GtkWidget *parent) {
 
   GtkWidget *panadapter_high_label=gtk_label_new(NULL);
   gtk_label_set_markup(GTK_LABEL(panadapter_high_label), "<b>Panadapter High: </b>");
-#ifdef GTK316
-  gtk_label_set_xalign(GTK_LABEL(panadapter_high_label),0);
-#endif
+  gtk_widget_set_halign(panadapter_high_label, GTK_ALIGN_START);
   gtk_widget_show(panadapter_high_label);
   gtk_grid_attach(GTK_GRID(grid),panadapter_high_label,col,row,1,1);
 
@@ -424,9 +420,7 @@ void tx_menu(GtkWidget *parent) {
 
   GtkWidget *panadapter_low_label=gtk_label_new(NULL);
   gtk_label_set_markup(GTK_LABEL(panadapter_low_label), "<b>Panadapter Low: </b>");
-#ifdef GTK316
-  gtk_label_set_xalign(GTK_LABEL(panadapter_low_label),0);
-#endif
+  gtk_widget_set_halign(panadapter_low_label, GTK_ALIGN_START);
   gtk_widget_show(panadapter_low_label);
   gtk_grid_attach(GTK_GRID(grid),panadapter_low_label,col,row,1,1);
 
@@ -434,9 +428,7 @@ void tx_menu(GtkWidget *parent) {
 
   GtkWidget *panadapter_step_label=gtk_label_new(NULL);
   gtk_label_set_markup(GTK_LABEL(panadapter_step_label), "<b>Panadapter Step: </b>");
-#ifdef GTK316
-  gtk_label_set_xalign(GTK_LABEL(panadapter_step_label),0);
-#endif
+  gtk_widget_set_halign(panadapter_step_label, GTK_ALIGN_START);
   gtk_widget_show(panadapter_step_label);
   gtk_grid_attach(GTK_GRID(grid),panadapter_step_label,col,row,1,1);
 
@@ -470,9 +462,7 @@ void tx_menu(GtkWidget *parent) {
 
   GtkWidget *am_carrier_level_label=gtk_label_new(NULL);
   gtk_label_set_markup(GTK_LABEL(am_carrier_level_label), "<b>AM Carrier Level:</b>");
-#ifdef GTK316
-  gtk_label_set_xalign(GTK_LABEL(am_carrier_level_label),0);
-#endif
+  gtk_widget_set_halign(am_carrier_level_label, GTK_ALIGN_START);
   gtk_widget_show(am_carrier_level_label);
   gtk_grid_attach(GTK_GRID(grid),am_carrier_level_label,col,row,1,1);
 
@@ -535,9 +525,7 @@ void tx_menu(GtkWidget *parent) {
   
   GtkWidget *tune_percent_label=gtk_label_new(NULL);
   gtk_label_set_markup(GTK_LABEL(tune_percent_label), "<b>Tune Percent:</b>");
-#ifdef GTK316
-  gtk_label_set_xalign(GTK_LABEL(tune_percent_label),0);
-#endif
+  gtk_widget_set_halign(tune_percent_label, GTK_ALIGN_START);
   gtk_widget_show(tune_percent_label);
   gtk_grid_attach(GTK_GRID(grid),tune_percent_label,col,row,1,1);
 
@@ -560,9 +548,7 @@ void tx_menu(GtkWidget *parent) {
 
   GtkWidget *swr_alarm_label=gtk_label_new(NULL);
   gtk_label_set_markup(GTK_LABEL(swr_alarm_label), "<b>SWR alarm at:</b>");
-#ifdef GTK316
-  gtk_label_set_xalign(GTK_LABEL(swr_alarm_label),0);
-#endif
+  gtk_widget_set_halign(swr_alarm_label, GTK_ALIGN_START);
   gtk_widget_show(swr_alarm_label);
   gtk_grid_attach(GTK_GRID(grid),swr_alarm_label,col,row,1,1);
 
index 59d9eed8fc91b9aa074179af40f2c81d6a845b46..ac0e11441e7f56837b9ba2f6793048f79324dfdb 100644 (file)
@@ -35,6 +35,7 @@
 #include "tx_panadapter.h"
 #include "vfo.h"
 #include "mode.h"
+#include "actions.h"
 #ifdef GPIO
 #include "gpio.h"
 #endif
@@ -352,6 +353,7 @@ void tx_panadapter_update(TRANSMITTER *tx) {
   cairo_set_line_width(cr, 1.0);
   cairo_stroke(cr);
 
+/*
 #ifdef GPIO
   if(controller==CONTROLLER1 && tx->dialog == NULL) {
     char text[64];
@@ -377,7 +379,7 @@ void tx_panadapter_update(TRANSMITTER *tx) {
     }
   }
 #endif
-
+*/
 
 #ifdef PURESIGNAL
   if(tx->puresignal) {
@@ -404,7 +406,7 @@ void tx_panadapter_update(TRANSMITTER *tx) {
     cairo_set_font_size(cr, DISPLAY_FONT_SIZE3);
 
     if(transmitter->fwd<0.0001) {
-      sprintf(text,"FWD %0.3f W",transmitter->exciter);
+      sprintf(text,"FWD %0.3f",transmitter->exciter);
     } else {
       static int max_count=0;
       static double max_level=0.0;
@@ -417,7 +419,6 @@ void tx_panadapter_update(TRANSMITTER *tx) {
     }
     cairo_move_to(cr,10,15);
     cairo_show_text(cr, text);
-
     //
     // Since colour is already red, no special
     // action for "high SWR" warning
@@ -426,7 +427,7 @@ void tx_panadapter_update(TRANSMITTER *tx) {
     cairo_move_to(cr,10,30);
     cairo_show_text(cr, text);
 
-    sprintf(text,"ALC %2.1f dB",transmitter->alc);
+    sprintf(text,"ALC %2.1f",transmitter->alc);
     cairo_move_to(cr,10,45);
     cairo_show_text(cr, text);
 
@@ -440,7 +441,6 @@ void tx_panadapter_update(TRANSMITTER *tx) {
     cairo_show_text(cr, text);
 */
   }
-
   //
   // If the SWR protection has been triggered, display message for three seconds
   //
@@ -475,6 +475,7 @@ void tx_panadapter_update(TRANSMITTER *tx) {
     cairo_move_to(cr, 160.0, 30.0);
     cairo_show_text(cr, text);
 
+
     if (tx_fifo_overrun || tx_fifo_underrun) {
       cairo_set_source_rgb(cr,1.0,0.0,0.0);
       if (tx_fifo_underrun) {
diff --git a/vfo.c b/vfo.c
index 174f4498110d22a2f096c3ccf46151babd5ea411..c78418f19112ae5567c1992d5610766efbf2fc99 100644 (file)
--- a/vfo.c
+++ b/vfo.c
@@ -1078,6 +1078,13 @@ void vfo_update() {
         long long af = vfo[0].ctun ? vfo[0].ctun_frequency : vfo[0].frequency;
         long long bf = vfo[1].ctun ? vfo[1].ctun_frequency : vfo[1].frequency;
 
+        if(vfo[0].entering_frequency) {
+            af=vfo[0].entered_frequency;
+        }
+        if(vfo[1].entering_frequency) {
+            bf=vfo[1].entered_frequency;
+        }
+
 #if 0
 //
 // DL1YCF: code still here but deactivated:
@@ -1115,7 +1122,9 @@ void vfo_update() {
             if (oob) sprintf(temp_text,"VFO A: Out of band");
             cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
         } else {
-            if(id==0) {
+            if(vfo[0].entering_frequency) {
+              cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
+            } else if(id==0) {
               cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
             } else {
               cairo_set_source_rgb(cr, 0.0, 0.65, 0.0);
@@ -1130,7 +1139,9 @@ void vfo_update() {
             if (oob) sprintf(temp_text,"VFO B: Out of band");
             cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
         } else {
-            if(id==1) {
+            if(vfo[1].entering_frequency) {
+              cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
+            } else if(id==1) {
               cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
             } else {
               cairo_set_source_rgb(cr, 0.0, 0.65, 0.0);
diff --git a/vfo.h b/vfo.h
index 77be2f495dd2c334a8f61e228fa91f21f626a3aa..a479d1739b4081e29e18019f4f58087a53cc8c92 100644 (file)
--- a/vfo.h
+++ b/vfo.h
@@ -44,6 +44,8 @@ struct _vfo {
   long long lo;
   long long offset;
 
+  gboolean entering_frequency;
+  long long entered_frequency;
 };
 
 extern struct _vfo vfo[MAX_VFOS];
@@ -73,6 +75,7 @@ typedef struct _set_frequency {
   long long frequency;
 } SET_FREQUENCY;
 
+#define STEPS 15
 extern int steps[];
 extern char *step_labels[];