From c41cb82ea7499c132546c1ab6f798deda6a9d7af Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 22 Dec 2017 16:55:39 +0100 Subject: Squashed 'hidapi/' content from commit b767b43f git-subtree-dir: hidapi git-subtree-split: b767b43f3e6f9c5b92ea7d738331deb8e03c4baf --- testgui/.gitignore | 20 + testgui/Makefile-manual | 26 + testgui/Makefile.am | 43 ++ testgui/Makefile.freebsd | 33 ++ testgui/Makefile.linux | 32 ++ testgui/Makefile.mac | 46 ++ testgui/Makefile.mingw | 32 ++ testgui/TestGUI.app.in/Contents/Info.plist | 28 ++ testgui/TestGUI.app.in/Contents/PkgInfo | 1 + .../Resources/English.lproj/InfoPlist.strings | Bin 0 -> 92 bytes .../Contents/Resources/Signal11.icns | Bin 0 -> 21918 bytes testgui/copy_to_bundle.sh | 97 ++++ testgui/mac_support.cpp | 134 ++++++ testgui/mac_support.h | 17 + testgui/mac_support_cocoa.m | 94 ++++ testgui/start.sh | 2 + testgui/test.cpp | 532 +++++++++++++++++++++ testgui/testgui.sln | 20 + testgui/testgui.vcproj | 217 +++++++++ 19 files changed, 1374 insertions(+) create mode 100644 testgui/.gitignore create mode 100644 testgui/Makefile-manual create mode 100644 testgui/Makefile.am create mode 100644 testgui/Makefile.freebsd create mode 100644 testgui/Makefile.linux create mode 100644 testgui/Makefile.mac create mode 100644 testgui/Makefile.mingw create mode 100644 testgui/TestGUI.app.in/Contents/Info.plist create mode 100644 testgui/TestGUI.app.in/Contents/PkgInfo create mode 100644 testgui/TestGUI.app.in/Contents/Resources/English.lproj/InfoPlist.strings create mode 100644 testgui/TestGUI.app.in/Contents/Resources/Signal11.icns create mode 100755 testgui/copy_to_bundle.sh create mode 100644 testgui/mac_support.cpp create mode 100644 testgui/mac_support.h create mode 100644 testgui/mac_support_cocoa.m create mode 100755 testgui/start.sh create mode 100644 testgui/test.cpp create mode 100644 testgui/testgui.sln create mode 100644 testgui/testgui.vcproj (limited to 'testgui') diff --git a/testgui/.gitignore b/testgui/.gitignore new file mode 100644 index 0000000..f989ea8 --- /dev/null +++ b/testgui/.gitignore @@ -0,0 +1,20 @@ +Debug +Release +*.exp +*.ilk +*.lib +*.suo +*.vcproj.* +*.ncb +*.suo +*.dll +*.pdb +*.o +hidapi-testgui +hidapi-hidraw-testgui +hidapi-libusb-testgui +.deps +.libs +*.la +*.lo +TestGUI.app diff --git a/testgui/Makefile-manual b/testgui/Makefile-manual new file mode 100644 index 0000000..3f61705 --- /dev/null +++ b/testgui/Makefile-manual @@ -0,0 +1,26 @@ + + +OS=$(shell uname) + +ifeq ($(OS), Darwin) + FILE=Makefile.mac +endif + +ifneq (,$(findstring MINGW,$(OS))) + FILE=Makefile.mingw +endif + +ifeq ($(OS), Linux) + FILE=Makefile.linux +endif + +ifeq ($(OS), FreeBSD) + FILE=Makefile.freebsd +endif + +ifeq ($(FILE), ) +all: + $(error Your platform ${OS} is not supported at this time.) +endif + +include $(FILE) diff --git a/testgui/Makefile.am b/testgui/Makefile.am new file mode 100644 index 0000000..1c02f3f --- /dev/null +++ b/testgui/Makefile.am @@ -0,0 +1,43 @@ + +AM_CPPFLAGS = -I$(top_srcdir)/hidapi/ $(CFLAGS_TESTGUI) + +if OS_LINUX +## Linux +bin_PROGRAMS = hidapi-hidraw-testgui hidapi-libusb-testgui + +hidapi_hidraw_testgui_SOURCES = test.cpp +hidapi_hidraw_testgui_LDADD = $(top_builddir)/linux/libhidapi-hidraw.la $(LIBS_TESTGUI) + +hidapi_libusb_testgui_SOURCES = test.cpp +hidapi_libusb_testgui_LDADD = $(top_builddir)/libusb/libhidapi-libusb.la $(LIBS_TESTGUI) +else +## Other OS's +bin_PROGRAMS = hidapi-testgui + +hidapi_testgui_SOURCES = test.cpp +hidapi_testgui_LDADD = $(top_builddir)/$(backend)/libhidapi.la $(LIBS_TESTGUI) +endif + +if OS_DARWIN +hidapi_testgui_SOURCES = test.cpp mac_support_cocoa.m mac_support.h +# Rules for copying the binary and its dependencies into the app bundle. +TestGUI.app/Contents/MacOS/hidapi-testgui$(EXEEXT): hidapi-testgui$(EXEEXT) + $(srcdir)/copy_to_bundle.sh + +all: all-am TestGUI.app/Contents/MacOS/hidapi-testgui$(EXEEXT) + +endif + +EXTRA_DIST = \ + copy_to_bundle.sh \ + Makefile-manual \ + Makefile.freebsd \ + Makefile.linux \ + Makefile.mac \ + Makefile.mingw \ + TestGUI.app.in \ + testgui.sln \ + testgui.vcproj + +distclean-local: + rm -rf TestGUI.app diff --git a/testgui/Makefile.freebsd b/testgui/Makefile.freebsd new file mode 100644 index 0000000..09a2473 --- /dev/null +++ b/testgui/Makefile.freebsd @@ -0,0 +1,33 @@ +########################################### +# Simple Makefile for HIDAPI test program +# +# Alan Ott +# Signal 11 Software +# 2010-06-01 +########################################### + +all: testgui + +CC=cc +CXX=c++ +COBJS=../libusb/hid.o +CPPOBJS=test.o +OBJS=$(COBJS) $(CPPOBJS) +CFLAGS=-I../hidapi -I/usr/local/include `fox-config --cflags` -Wall -g -c +LDFLAGS= -L/usr/local/lib +LIBS= -lusb -liconv `fox-config --libs` -pthread + + +testgui: $(OBJS) + $(CXX) -Wall -g $^ $(LDFLAGS) -o $@ $(LIBS) + +$(COBJS): %.o: %.c + $(CC) $(CFLAGS) $< -o $@ + +$(CPPOBJS): %.o: %.cpp + $(CXX) $(CFLAGS) $< -o $@ + +clean: + rm *.o testgui + +.PHONY: clean diff --git a/testgui/Makefile.linux b/testgui/Makefile.linux new file mode 100644 index 0000000..d32e163 --- /dev/null +++ b/testgui/Makefile.linux @@ -0,0 +1,32 @@ +########################################### +# Simple Makefile for HIDAPI test program +# +# Alan Ott +# Signal 11 Software +# 2010-06-01 +########################################### + +all: testgui + +CC=gcc +CXX=g++ +COBJS=../libusb/hid.o +CPPOBJS=test.o +OBJS=$(COBJS) $(CPPOBJS) +CFLAGS=-I../hidapi -Wall -g -c `fox-config --cflags` `pkg-config libusb-1.0 --cflags` +LIBS=-ludev -lrt -lpthread `fox-config --libs` `pkg-config libusb-1.0 --libs` + + +testgui: $(OBJS) + g++ -Wall -g $^ $(LIBS) -o testgui + +$(COBJS): %.o: %.c + $(CC) $(CFLAGS) $< -o $@ + +$(CPPOBJS): %.o: %.cpp + $(CXX) $(CFLAGS) $< -o $@ + +clean: + rm *.o testgui + +.PHONY: clean diff --git a/testgui/Makefile.mac b/testgui/Makefile.mac new file mode 100644 index 0000000..cda7d49 --- /dev/null +++ b/testgui/Makefile.mac @@ -0,0 +1,46 @@ +########################################### +# Simple Makefile for HIDAPI test program +# +# Alan Ott +# Signal 11 Software +# 2010-07-03 +########################################### + +all: hidapi-testgui + +CC=gcc +CXX=g++ +COBJS=../mac/hid.o +CPPOBJS=test.o +OBJCOBJS=mac_support_cocoa.o +OBJS=$(COBJS) $(CPPOBJS) $(OBJCOBJS) +CFLAGS=-I../hidapi -Wall -g -c `fox-config --cflags` +LDFLAGS=-L/usr/X11R6/lib +LIBS=`fox-config --libs` -framework IOKit -framework CoreFoundation -framework Cocoa + + +hidapi-testgui: $(OBJS) TestGUI.app + g++ -Wall -g $(OBJS) $(LIBS) $(LDFLAGS) -o hidapi-testgui + ./copy_to_bundle.sh + #cp TestGUI.app/Contents/MacOS/hidapi-testgui TestGUI.app/Contents/MacOS/tg + #cp start.sh TestGUI.app/Contents/MacOS/hidapi-testgui + +$(COBJS): %.o: %.c + $(CC) $(CFLAGS) $< -o $@ + +$(CPPOBJS): %.o: %.cpp + $(CXX) $(CFLAGS) $< -o $@ + +$(OBJCOBJS): %.o: %.m + $(CXX) $(CFLAGS) -x objective-c++ $< -o $@ + +TestGUI.app: TestGUI.app.in + rm -Rf TestGUI.app + mkdir -p TestGUI.app + cp -R TestGUI.app.in/ TestGUI.app + +clean: + rm -f $(OBJS) hidapi-testgui + rm -Rf TestGUI.app + +.PHONY: clean diff --git a/testgui/Makefile.mingw b/testgui/Makefile.mingw new file mode 100644 index 0000000..df0f69d --- /dev/null +++ b/testgui/Makefile.mingw @@ -0,0 +1,32 @@ +########################################### +# Simple Makefile for HIDAPI test program +# +# Alan Ott +# Signal 11 Software +# 2010-06-01 +########################################### + +all: hidapi-testgui + +CC=gcc +CXX=g++ +COBJS=../windows/hid.o +CPPOBJS=test.o +OBJS=$(COBJS) $(CPPOBJS) +CFLAGS=-I../hidapi -I../../hidapi-externals/fox/include -g -c +LIBS= -mwindows -lsetupapi -L../../hidapi-externals/fox/lib -Wl,-Bstatic -lFOX-1.6 -Wl,-Bdynamic -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32 + + +hidapi-testgui: $(OBJS) + g++ -g $^ $(LIBS) -o hidapi-testgui + +$(COBJS): %.o: %.c + $(CC) $(CFLAGS) $< -o $@ + +$(CPPOBJS): %.o: %.cpp + $(CXX) $(CFLAGS) $< -o $@ + +clean: + rm -f *.o hidapi-testgui.exe + +.PHONY: clean diff --git a/testgui/TestGUI.app.in/Contents/Info.plist b/testgui/TestGUI.app.in/Contents/Info.plist new file mode 100644 index 0000000..ab473d5 --- /dev/null +++ b/testgui/TestGUI.app.in/Contents/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + + CFBundleExecutable + hidapi-testgui + CFBundleIconFile + Signal11.icns + CFBundleIdentifier + us.signal11.hidtestgui + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + testgui + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + CSResourcesFileMapped + + + diff --git a/testgui/TestGUI.app.in/Contents/PkgInfo b/testgui/TestGUI.app.in/Contents/PkgInfo new file mode 100644 index 0000000..bd04210 --- /dev/null +++ b/testgui/TestGUI.app.in/Contents/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/testgui/TestGUI.app.in/Contents/Resources/English.lproj/InfoPlist.strings b/testgui/TestGUI.app.in/Contents/Resources/English.lproj/InfoPlist.strings new file mode 100644 index 0000000..dea12de Binary files /dev/null and b/testgui/TestGUI.app.in/Contents/Resources/English.lproj/InfoPlist.strings differ diff --git a/testgui/TestGUI.app.in/Contents/Resources/Signal11.icns b/testgui/TestGUI.app.in/Contents/Resources/Signal11.icns new file mode 100644 index 0000000..bb6b7bd Binary files /dev/null and b/testgui/TestGUI.app.in/Contents/Resources/Signal11.icns differ diff --git a/testgui/copy_to_bundle.sh b/testgui/copy_to_bundle.sh new file mode 100755 index 0000000..f0fc767 --- /dev/null +++ b/testgui/copy_to_bundle.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +#### Configuration: +# The name of the executable. It is assumed +# that it is in the current working directory. +EXE_NAME=hidapi-testgui +# Path to the executable directory inside the bundle. +# This must be an absolute path, so use $PWD. +EXEPATH=$PWD/TestGUI.app/Contents/MacOS +# Libraries to explicitly bundle, even though they +# may not be in /opt/local. One per line. These +# are used with grep, so only a portion of the name +# is required. eg: libFOX, libz, etc. +LIBS_TO_BUNDLE=libFOX + + +function copydeps { + local file=$1 + # echo "Copying deps for $file...." + local BASE_OF_EXE=`basename $file` + + # A will contain the dependencies of this library + local A=`otool -LX $file |cut -f 1 -d " "` + local i + for i in $A; do + local BASE=`basename $i` + + # See if it's a lib we specifically want to bundle + local bundle_this_lib=0 + local j + for j in $LIBS_TO_BUNDLE; do + echo $i |grep -q $j + if [ $? -eq 0 ]; then + bundle_this_lib=1 + echo "bundling $i because it's in the list." + break; + fi + done + + # See if it's in /opt/local. Bundle all in /opt/local + local isOptLocal=0 + echo $i |grep -q /opt/local + if [ $? -eq 0 ]; then + isOptLocal=1 + echo "bundling $i because it's in /opt/local." + fi + + # Bundle the library + if [ $isOptLocal -ne 0 ] || [ $bundle_this_lib -ne 0 ]; then + + # Copy the file into the bundle if it exists. + if [ -f $EXEPATH/$BASE ]; then + z=0 + else + cp $i $EXEPATH + chmod 755 $EXEPATH/$BASE + fi + + + # echo "$BASE_OF_EXE depends on $BASE" + + # Fix the paths using install_name_tool and then + # call this function recursively for each dependency + # of this library. + if [ $BASE_OF_EXE != $BASE ]; then + + # Fix the paths + install_name_tool -id @executable_path/$BASE $EXEPATH/$BASE + install_name_tool -change $i @executable_path/$BASE $EXEPATH/$BASE_OF_EXE + + # Call this function (recursive) on + # on each dependency of this library. + copydeps $EXEPATH/$BASE + fi + fi + done +} + +rm -f $EXEPATH/* + +# Copy the binary into the bundle. Use ../libtool to do this if it's +# available beacuse if $EXE_NAME was built with autotools, it will be +# necessary. If ../libtool not available, just use cp to do the copy, but +# only if $EXE_NAME is a binary. +if [ -x ../libtool ]; then + ../libtool --mode=install cp $EXE_NAME $EXEPATH +else + file -bI $EXE_NAME |grep binary + if [ $? -ne 0 ]; then + echo "There is no ../libtool and $EXE_NAME is not a binary." + echo "I'm not sure what to do." + exit 1 + else + cp $EXE_NAME $EXEPATH + fi +fi +copydeps $EXEPATH/$EXE_NAME diff --git a/testgui/mac_support.cpp b/testgui/mac_support.cpp new file mode 100644 index 0000000..e1e3874 --- /dev/null +++ b/testgui/mac_support.cpp @@ -0,0 +1,134 @@ +/******************************* + Mac support for HID Test GUI + + Alan Ott + Signal 11 Software + + Some of this code is from Apple Documentation, most notably + http://developer.apple.com/legacy/mac/library/documentation/AppleScript/Conceptual/AppleEvents/AppleEvents.pdf +*******************************/ + +#include +#include + + +extern FXMainWindow *g_main_window; + +static pascal OSErr HandleQuitMessage(const AppleEvent *theAppleEvent, AppleEvent + *reply, long handlerRefcon) +{ + puts("Quitting\n"); + FXApp::instance()->exit(); + return 0; +} + +static pascal OSErr HandleReopenMessage(const AppleEvent *theAppleEvent, AppleEvent + *reply, long handlerRefcon) +{ + puts("Showing"); + g_main_window->show(); + return 0; +} + +static pascal OSErr HandleWildCardMessage(const AppleEvent *theAppleEvent, AppleEvent + *reply, long handlerRefcon) +{ + puts("WildCard\n"); + return 0; +} + +OSStatus AEHandler(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon) +{ + Boolean release = false; + EventRecord eventRecord; + OSErr ignoreErrForThisSample; + + // Events of type kEventAppleEvent must be removed from the queue + // before being passed to AEProcessAppleEvent. + if (IsEventInQueue(GetMainEventQueue(), inEvent)) + { + // RemoveEventFromQueue will release the event, which will + // destroy it if we don't retain it first. + RetainEvent(inEvent); + release = true; + RemoveEventFromQueue(GetMainEventQueue(), inEvent); + } + // Convert the event ref to the type AEProcessAppleEvent expects. + ConvertEventRefToEventRecord(inEvent, &eventRecord); + ignoreErrForThisSample = AEProcessAppleEvent(&eventRecord); + if (release) + ReleaseEvent(inEvent); + // This Carbon event has been handled, even if no AppleEvent handlers + // were installed for the Apple event. + return noErr; +} + +static void HandleEvent(EventRecord *event) +{ + //printf("What: %d message %x\n", event->what, event->message); + if (event->what == osEvt) { + if (((event->message >> 24) & 0xff) == suspendResumeMessage) { + if (event->message & resumeFlag) { + g_main_window->show(); + } + } + } + +#if 0 + switch (event->what) + { + case mouseDown: + //HandleMouseDown(event); + break; + case keyDown: + case autoKey: + //HandleKeyPress(event); + break; + case kHighLevelEvent: + puts("Calling ProcessAppleEvent\n"); + AEProcessAppleEvent(event); + break; + } +#endif +} + +void +init_apple_message_system() +{ + OSErr err; + static const EventTypeSpec appleEvents[] = + { + { kEventClassAppleEvent, kEventAppleEvent } + }; + + /* Install the handler for Apple Events */ + InstallApplicationEventHandler(NewEventHandlerUPP(AEHandler), + GetEventTypeCount(appleEvents), appleEvents, 0, NULL); + + /* Install handlers for the individual Apple Events that come + from the Dock icon: the Reopen (click), and the Quit messages. */ + err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, + NewAEEventHandlerUPP(HandleQuitMessage), 0, false); + err = AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, + NewAEEventHandlerUPP(HandleReopenMessage), 0, false); +#if 0 + // Left as an example of a wild card match. + err = AEInstallEventHandler(kCoreEventClass, typeWildCard, + NewAEEventHandlerUPP(HandleWildMessage), 0, false); +#endif +} + +void +check_apple_events() +{ + RgnHandle cursorRgn = NULL; + Boolean gotEvent=TRUE; + EventRecord event; + + while (gotEvent) { + gotEvent = WaitNextEvent(everyEvent, &event, 0L/*timeout*/, cursorRgn); + if (gotEvent) { + HandleEvent(&event); + } + } +} diff --git a/testgui/mac_support.h b/testgui/mac_support.h new file mode 100644 index 0000000..7d9ab49 --- /dev/null +++ b/testgui/mac_support.h @@ -0,0 +1,17 @@ +/******************************* + Mac support for HID Test GUI + + Alan Ott + Signal 11 Software + +*******************************/ + +#ifndef MAC_SUPPORT_H__ +#define MAC_SUPPORT_H__ + +extern "C" { + void init_apple_message_system(); + void check_apple_events(); +} + +#endif diff --git a/testgui/mac_support_cocoa.m b/testgui/mac_support_cocoa.m new file mode 100644 index 0000000..75de7e9 --- /dev/null +++ b/testgui/mac_support_cocoa.m @@ -0,0 +1,94 @@ +/******************************* + Mac support for HID Test GUI + + Alan Ott + Signal 11 Software +*******************************/ + +#include +#import + +extern FXMainWindow *g_main_window; + + +@interface MyAppDelegate : NSObject +{ +} +@end + +@implementation MyAppDelegate +- (void) applicationWillBecomeActive:(NSNotification*)notif +{ + printf("WillBecomeActive\n"); + g_main_window->show(); + +} + +- (void) applicationWillTerminate:(NSNotification*)notif +{ + /* Doesn't get called. Not sure why */ + printf("WillTerminate\n"); + FXApp::instance()->exit(); +} + +- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender +{ + /* Doesn't get called. Not sure why */ + printf("ShouldTerminate\n"); + return YES; +} + +- (void) applicationWillHide:(NSNotification*)notif +{ + printf("WillHide\n"); + g_main_window->hide(); +} + +- (void) handleQuitEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent +{ + printf("QuitEvent\n"); + FXApp::instance()->exit(); +} + +@end + +extern "C" { + +void +init_apple_message_system() +{ + static MyAppDelegate *d = [MyAppDelegate new]; + + [[NSApplication sharedApplication] setDelegate:d]; + + /* Register for Apple Events. */ + /* This is from + http://stackoverflow.com/questions/1768497/application-exit-event */ + NSAppleEventManager *aem = [NSAppleEventManager sharedAppleEventManager]; + [aem setEventHandler:d + andSelector:@selector(handleQuitEvent:withReplyEvent:) + forEventClass:kCoreEventClass andEventID:kAEQuitApplication]; +} + +void +check_apple_events() +{ + NSApplication *app = [NSApplication sharedApplication]; + + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + while (1) { + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:nil + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event == NULL) + break; + else { + //printf("Event happened: Type: %d\n", event->_type); + [app sendEvent: event]; + } + } + [pool release]; +} + +} /* extern "C" */ diff --git a/testgui/start.sh b/testgui/start.sh new file mode 100755 index 0000000..980635d --- /dev/null +++ b/testgui/start.sh @@ -0,0 +1,2 @@ +#!/bin/bash +xterm -e /Users/alan/work/hidapi/testgui/TestGUI.app/Contents/MacOS/tg diff --git a/testgui/test.cpp b/testgui/test.cpp new file mode 100644 index 0000000..538db79 --- /dev/null +++ b/testgui/test.cpp @@ -0,0 +1,532 @@ +/******************************************************* + Demo Program for HIDAPI + + Alan Ott + Signal 11 Software + + 2010-07-20 + + Copyright 2010, All Rights Reserved + + This contents of this file may be used by anyone + for any reason without any conditions and may be + used as a starting point for your own applications + which use HIDAPI. +********************************************************/ + + +#include + +#include "hidapi.h" +#include "mac_support.h" +#include +#include +#include + +#ifdef _WIN32 + // Thanks Microsoft, but I know how to use strncpy(). + #pragma warning(disable:4996) +#endif + +class MainWindow : public FXMainWindow { + FXDECLARE(MainWindow) + +public: + enum { + ID_FIRST = FXMainWindow::ID_LAST, + ID_CONNECT, + ID_DISCONNECT, + ID_RESCAN, + ID_SEND_OUTPUT_REPORT, + ID_SEND_FEATURE_REPORT, + ID_GET_FEATURE_REPORT, + ID_CLEAR, + ID_TIMER, + ID_MAC_TIMER, + ID_LAST, + }; + +private: + FXList *device_list; + FXButton *connect_button; + FXButton *disconnect_button; + FXButton *rescan_button; + FXButton *output_button; + FXLabel *connected_label; + FXTextField *output_text; + FXTextField *output_len; + FXButton *feature_button; + FXButton *get_feature_button; + FXTextField *feature_text; + FXTextField *feature_len; + FXTextField *get_feature_text; + FXText *input_text; + FXFont *title_font; + + struct hid_device_info *devices; + hid_device *connected_device; + size_t getDataFromTextField(FXTextField *tf, char *buf, size_t len); + int getLengthFromTextField(FXTextField *tf); + + +protected: + MainWindow() {}; +public: + MainWindow(FXApp *a); + ~MainWindow(); + virtual void create(); + + long onConnect(FXObject *sender, FXSelector sel, void *ptr); + long onDisconnect(FXObject *sender, FXSelector sel, void *ptr); + long onRescan(FXObject *sender, FXSelector sel, void *ptr); + long onSendOutputReport(FXObject *sender, FXSelector sel, void *ptr); + long onSendFeatureReport(FXObject *sender, FXSelector sel, void *ptr); + long onGetFeatureReport(FXObject *sender, FXSelector sel, void *ptr); + long onClear(FXObject *sender, FXSelector sel, void *ptr); + long onTimeout(FXObject *sender, FXSelector sel, void *ptr); + long onMacTimeout(FXObject *sender, FXSelector sel, void *ptr); +}; + +// FOX 1.7 changes the timeouts to all be nanoseconds. +// Fox 1.6 had all timeouts as milliseconds. +#if (FOX_MINOR >= 7) + const int timeout_scalar = 1000*1000; +#else + const int timeout_scalar = 1; +#endif + +FXMainWindow *g_main_window; + + +FXDEFMAP(MainWindow) MainWindowMap [] = { + FXMAPFUNC(SEL_COMMAND, MainWindow::ID_CONNECT, MainWindow::onConnect ), + FXMAPFUNC(SEL_COMMAND, MainWindow::ID_DISCONNECT, MainWindow::onDisconnect ), + FXMAPFUNC(SEL_COMMAND, MainWindow::ID_RESCAN, MainWindow::onRescan ), + FXMAPFUNC(SEL_COMMAND, MainWindow::ID_SEND_OUTPUT_REPORT, MainWindow::onSendOutputReport ), + FXMAPFUNC(SEL_COMMAND, MainWindow::ID_SEND_FEATURE_REPORT, MainWindow::onSendFeatureReport ), + FXMAPFUNC(SEL_COMMAND, MainWindow::ID_GET_FEATURE_REPORT, MainWindow::onGetFeatureReport ), + FXMAPFUNC(SEL_COMMAND, MainWindow::ID_CLEAR, MainWindow::onClear ), + FXMAPFUNC(SEL_TIMEOUT, MainWindow::ID_TIMER, MainWindow::onTimeout ), + FXMAPFUNC(SEL_TIMEOUT, MainWindow::ID_MAC_TIMER, MainWindow::onMacTimeout ), +}; + +FXIMPLEMENT(MainWindow, FXMainWindow, MainWindowMap, ARRAYNUMBER(MainWindowMap)); + +MainWindow::MainWindow(FXApp *app) + : FXMainWindow(app, "HIDAPI Test Application", NULL, NULL, DECOR_ALL, 200,100, 425,700) +{ + devices = NULL; + connected_device = NULL; + + FXVerticalFrame *vf = new FXVerticalFrame(this, LAYOUT_FILL_Y|LAYOUT_FILL_X); + + FXLabel *label = new FXLabel(vf, "HIDAPI Test Tool"); + title_font = new FXFont(getApp(), "Arial", 14, FXFont::Bold); + label->setFont(title_font); + + new FXLabel(vf, + "Select a device and press Connect.", NULL, JUSTIFY_LEFT); + new FXLabel(vf, + "Output data bytes can be entered in the Output section, \n" + "separated by space, comma or brackets. Data starting with 0x\n" + "is treated as hex. Data beginning with a 0 is treated as \n" + "octal. All other data is treated as decimal.", NULL, JUSTIFY_LEFT); + new FXLabel(vf, + "Data received from the device appears in the Input section.", + NULL, JUSTIFY_LEFT); + new FXLabel(vf, + "Optionally, a report length may be specified. Extra bytes are\n" + "padded with zeros. If no length is specified, the length is \n" + "inferred from the data.", + NULL, JUSTIFY_LEFT); + new FXLabel(vf, ""); + + // Device List and Connect/Disconnect buttons + FXHorizontalFrame *hf = new FXHorizontalFrame(vf, LAYOUT_FILL_X); + //device_list = new FXList(new FXHorizontalFrame(hf,FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0), NULL, 0, LISTBOX_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0,0,300,200); + device_list = new FXList(new FXHorizontalFrame(hf,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0), NULL, 0, LISTBOX_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,300,200); + FXVerticalFrame *buttonVF = new FXVerticalFrame(hf); + connect_button = new FXButton(buttonVF, "Connect", NULL, this, ID_CONNECT, BUTTON_NORMAL|LAYOUT_FILL_X); + disconnect_button = new FXButton(buttonVF, "Disconnect", NULL, this, ID_DISCONNECT, BUTTON_NORMAL|LAYOUT_FILL_X); + disconnect_button->disable(); + rescan_button = new FXButton(buttonVF, "Re-Scan devices", NULL, this, ID_RESCAN, BUTTON_NORMAL|LAYOUT_FILL_X); + new FXHorizontalFrame(buttonVF, 0, 0,0,0,0, 0,0,50,0); + + connected_label = new FXLabel(vf, "Disconnected"); + + new FXHorizontalFrame(vf); + + // Output Group Box + FXGroupBox *gb = new FXGroupBox(vf, "Output", FRAME_GROOVE|LAYOUT_FILL_X); + FXMatrix *matrix = new FXMatrix(gb, 3, MATRIX_BY_COLUMNS|LAYOUT_FILL_X); + new FXLabel(matrix, "Data"); + new FXLabel(matrix, "Length"); + new FXLabel(matrix, ""); + + //hf = new FXHorizontalFrame(gb, LAYOUT_FILL_X); + output_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); + output_text->setText("1 0x81 0"); + output_len = new FXTextField(matrix, 5, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); + output_button = new FXButton(matrix, "Send Output Report", NULL, this, ID_SEND_OUTPUT_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); + output_button->disable(); + //new FXHorizontalFrame(matrix, LAYOUT_FILL_X); + + //hf = new FXHorizontalFrame(gb, LAYOUT_FILL_X); + feature_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); + feature_len = new FXTextField(matrix, 5, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); + feature_button = new FXButton(matrix, "Send Feature Report", NULL, this, ID_SEND_FEATURE_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); + feature_button->disable(); + + get_feature_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); + new FXWindow(matrix); + get_feature_button = new FXButton(matrix, "Get Feature Report", NULL, this, ID_GET_FEATURE_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); + get_feature_button->disable(); + + + // Input Group Box + gb = new FXGroupBox(vf, "Input", FRAME_GROOVE|LAYOUT_FILL_X|LAYOUT_FILL_Y); + FXVerticalFrame *innerVF = new FXVerticalFrame(gb, LAYOUT_FILL_X|LAYOUT_FILL_Y); + input_text = new FXText(new FXHorizontalFrame(innerVF,LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0), NULL, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y); + input_text->setEditable(false); + new FXButton(innerVF, "Clear", NULL, this, ID_CLEAR, BUTTON_NORMAL|LAYOUT_RIGHT); + + +} + +MainWindow::~MainWindow() +{ + if (connected_device) + hid_close(connected_device); + hid_exit(); + delete title_font; +} + +void +MainWindow::create() +{ + FXMainWindow::create(); + show(); + + onRescan(NULL, 0, NULL); + + +#ifdef __APPLE__ + init_apple_message_system(); +#endif + + getApp()->addTimeout(this, ID_MAC_TIMER, + 50 * timeout_scalar /*50ms*/); +} + +long +MainWindow::onConnect(FXObject *sender, FXSelector sel, void *ptr) +{ + if (connected_device != NULL) + return 1; + + FXint cur_item = device_list->getCurrentItem(); + if (cur_item < 0) + return -1; + FXListItem *item = device_list->getItem(cur_item); + if (!item) + return -1; + struct hid_device_info *device_info = (struct hid_device_info*) item->getData(); + if (!device_info) + return -1; + + connected_device = hid_open_path(device_info->path); + + if (!connected_device) { + FXMessageBox::error(this, MBOX_OK, "Device Error", "Unable To Connect to Device"); + return -1; + } + + hid_set_nonblocking(connected_device, 1); + + getApp()->addTimeout(this, ID_TIMER, + 5 * timeout_scalar /*5ms*/); + + FXString s; + s.format("Connected to: %04hx:%04hx -", device_info->vendor_id, device_info->product_id); + s += FXString(" ") + device_info->manufacturer_string; + s += FXString(" ") + device_info->product_string; + connected_label->setText(s); + output_button->enable(); + feature_button->enable(); + get_feature_button->enable(); + connect_button->disable(); + disconnect_button->enable(); + input_text->setText(""); + + + return 1; +} + +long +MainWindow::onDisconnect(FXObject *sender, FXSelector sel, void *ptr) +{ + hid_close(connected_device); + connected_device = NULL; + connected_label->setText("Disconnected"); + output_button->disable(); + feature_button->disable(); + get_feature_button->disable(); + connect_button->enable(); + disconnect_button->disable(); + + getApp()->removeTimeout(this, ID_TIMER); + + return 1; +} + +long +MainWindow::onRescan(FXObject *sender, FXSelector sel, void *ptr) +{ + struct hid_device_info *cur_dev; + + device_list->clearItems(); + + // List the Devices + hid_free_enumeration(devices); + devices = hid_enumerate(0x0, 0x0); + cur_dev = devices; + while (cur_dev) { + // Add it to the List Box. + FXString s; + FXString usage_str; + s.format("%04hx:%04hx -", cur_dev->vendor_id, cur_dev->product_id); + s += FXString(" ") + cur_dev->manufacturer_string; + s += FXString(" ") + cur_dev->product_string; + usage_str.format(" (usage: %04hx:%04hx) ", cur_dev->usage_page, cur_dev->usage); + s += usage_str; + FXListItem *li = new FXListItem(s, NULL, cur_dev); + device_list->appendItem(li); + + cur_dev = cur_dev->next; + } + + if (device_list->getNumItems() == 0) + device_list->appendItem("*** No Devices Connected ***"); + else { + device_list->selectItem(0); + } + + return 1; +} + +size_t +MainWindow::getDataFromTextField(FXTextField *tf, char *buf, size_t len) +{ + const char *delim = " ,{}\t\r\n"; + FXString data = tf->getText(); + const FXchar *d = data.text(); + size_t i = 0; + + // Copy the string from the GUI. + size_t sz = strlen(d); + char *str = (char*) malloc(sz+1); + strcpy(str, d); + + // For each token in the string, parse and store in buf[]. + char *token = strtok(str, delim); + while (token) { + char *endptr; + long int val = strtol(token, &endptr, 0); + buf[i++] = val; + token = strtok(NULL, delim); + } + + free(str); + return i; +} + +/* getLengthFromTextField() + Returns length: + 0: empty text field + >0: valid length + -1: invalid length */ +int +MainWindow::getLengthFromTextField(FXTextField *tf) +{ + long int len; + FXString str = tf->getText(); + size_t sz = str.length(); + + if (sz > 0) { + char *endptr; + len = strtol(str.text(), &endptr, 0); + if (endptr != str.text() && *endptr == '\0') { + if (len <= 0) { + FXMessageBox::error(this, MBOX_OK, "Invalid length", "Enter a length greater than zero."); + return -1; + } + return len; + } + else + return -1; + } + + return 0; +} + +long +MainWindow::onSendOutputReport(FXObject *sender, FXSelector sel, void *ptr) +{ + char buf[256]; + size_t data_len, len; + int textfield_len; + + memset(buf, 0x0, sizeof(buf)); + textfield_len = getLengthFromTextField(output_len); + data_len = getDataFromTextField(output_text, buf, sizeof(buf)); + + if (textfield_len < 0) { + FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is invalid. Please enter a number in hex, octal, or decimal."); + return 1; + } + + if (textfield_len > sizeof(buf)) { + FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is too long."); + return 1; + } + + len = (textfield_len)? textfield_len: data_len; + + int res = hid_write(connected_device, (const unsigned char*)buf, len); + if (res < 0) { + FXMessageBox::error(this, MBOX_OK, "Error Writing", "Could not write to device. Error reported was: %ls", hid_error(connected_device)); + } + + return 1; +} + +long +MainWindow::onSendFeatureReport(FXObject *sender, FXSelector sel, void *ptr) +{ + char buf[256]; + size_t data_len, len; + int textfield_len; + + memset(buf, 0x0, sizeof(buf)); + textfield_len = getLengthFromTextField(feature_len); + data_len = getDataFromTextField(feature_text, buf, sizeof(buf)); + + if (textfield_len < 0) { + FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is invalid. Please enter a number in hex, octal, or decimal."); + return 1; + } + + if (textfield_len > sizeof(buf)) { + FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is too long."); + return 1; + } + + len = (textfield_len)? textfield_len: data_len; + + int res = hid_send_feature_report(connected_device, (const unsigned char*)buf, len); + if (res < 0) { + FXMessageBox::error(this, MBOX_OK, "Error Writing", "Could not send feature report to device. Error reported was: %ls", hid_error(connected_device)); + } + + return 1; +} + +long +MainWindow::onGetFeatureReport(FXObject *sender, FXSelector sel, void *ptr) +{ + char buf[256]; + size_t len; + + memset(buf, 0x0, sizeof(buf)); + len = getDataFromTextField(get_feature_text, buf, sizeof(buf)); + + if (len != 1) { + FXMessageBox::error(this, MBOX_OK, "Too many numbers", "Enter only a single report number in the text field"); + } + + int res = hid_get_feature_report(connected_device, (unsigned char*)buf, sizeof(buf)); + if (res < 0) { + FXMessageBox::error(this, MBOX_OK, "Error Getting Report", "Could not get feature report from device. Error reported was: %ls", hid_error(connected_device)); + } + + if (res > 0) { + FXString s; + s.format("Returned Feature Report. %d bytes:\n", res); + for (int i = 0; i < res; i++) { + FXString t; + t.format("%02hhx ", buf[i]); + s += t; + if ((i+1) % 4 == 0) + s += " "; + if ((i+1) % 16 == 0) + s += "\n"; + } + s += "\n"; + input_text->appendText(s); + input_text->setBottomLine(INT_MAX); + } + + return 1; +} + +long +MainWindow::onClear(FXObject *sender, FXSelector sel, void *ptr) +{ + input_text->setText(""); + return 1; +} + +long +MainWindow::onTimeout(FXObject *sender, FXSelector sel, void *ptr) +{ + unsigned char buf[256]; + int res = hid_read(connected_device, buf, sizeof(buf)); + + if (res > 0) { + FXString s; + s.format("Received %d bytes:\n", res); + for (int i = 0; i < res; i++) { + FXString t; + t.format("%02hhx ", buf[i]); + s += t; + if ((i+1) % 4 == 0) + s += " "; + if ((i+1) % 16 == 0) + s += "\n"; + } + s += "\n"; + input_text->appendText(s); + input_text->setBottomLine(INT_MAX); + } + if (res < 0) { + input_text->appendText("hid_read() returned error\n"); + input_text->setBottomLine(INT_MAX); + } + + getApp()->addTimeout(this, ID_TIMER, + 5 * timeout_scalar /*5ms*/); + return 1; +} + +long +MainWindow::onMacTimeout(FXObject *sender, FXSelector sel, void *ptr) +{ +#ifdef __APPLE__ + check_apple_events(); + + getApp()->addTimeout(this, ID_MAC_TIMER, + 50 * timeout_scalar /*50ms*/); +#endif + + return 1; +} + +int main(int argc, char **argv) +{ + FXApp app("HIDAPI Test Application", "Signal 11 Software"); + app.init(argc, argv); + g_main_window = new MainWindow(&app); + app.create(); + app.run(); + return 0; +} diff --git a/testgui/testgui.sln b/testgui/testgui.sln new file mode 100644 index 0000000..56be6c6 --- /dev/null +++ b/testgui/testgui.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testgui", "testgui.vcproj", "{08769AC3-785A-4DDC-BFC7-1775414B7AB7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.ActiveCfg = Debug|Win32 + {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.Build.0 = Debug|Win32 + {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.ActiveCfg = Release|Win32 + {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/testgui/testgui.vcproj b/testgui/testgui.vcproj new file mode 100644 index 0000000..0f73a01 --- /dev/null +++ b/testgui/testgui.vcproj @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3