diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..9801c18 --- /dev/null +++ b/README.txt @@ -0,0 +1,43 @@ +======================================================== + +This folder contains Arduino code and host software to +demonstrate HID-class USB serial communication for AVRs +using V-USB. The code and circuit design are based on V-USB +http://www.obdev.at/products/vusb/index.html +and USnoobie. It is designed to work for ATmega328 but +can be adapted to other AVR microcontrollers as well. + +The software is written by Ray Wang at Rayshobby LLC +and published under the Creative Commons Attribution- +ShareAlike (CC-SA) 3.0 license. + +======================================================== + +The folders are organized as follows: + +- 'schematic' contains the circuit schematic and part list. + +- 'bootloader' contains the modified USnoobie bootloader + The bootloader is optional, but once flashed, it allows + the mcu to bootload (by pressing the button during power-on + or reset) as a usbasp programmer, so you will not need + any external programer to flash a program. + +- 'arduino code' contains the Arduino library for HIDSerial. + To use it: + * copy boards.txt (from hardware/arduino folder) + to the corresponding folder in your arduino installation + directory, and the HIDSerial library to your arduino's + libraries directory. + * Run Arduino (the recommended version is 1.0.5 or 1.0.4). + * Make sure you select 'USnoobie' from Tools -> Boards + * If you use the USnoobie bootloader, select 'USBasp' from + Tools -> Programmer. + * Select any provided example from File -> Examples -> HIDSerial + * Upload + +- 'host software' contains the standalone applications and Processing + source code for HIDSerialMonitor. Please check the README.txt + therein. + +======================================================== diff --git a/arduino code/hardware/arduino/boards.txt b/arduino code/hardware/arduino/boards.txt new file mode 100644 index 0000000..ce0a4d7 --- /dev/null +++ b/arduino code/hardware/arduino/boards.txt @@ -0,0 +1,586 @@ +# See: http://code.google.com/p/arduino/wiki/Platforms + +############################################################## + +usnoobie.name=USnooBie (USBaspLoader ATmega328P at 12MHz) +usnoobie.upload.protocol=usbasp +usnoobie.upload.maximum_size=28672 +usnoobie.upload.speed=115200 +usnoobie.upload.disable_flushing=true +usnoobie.bootloader.low_fuses=0xFF +usnoobie.bootloader.high_fuses=0xD8 +usnoobie.bootloader.extended_fuses=0xFF +usnoobie.bootloader.path=usnoobie +usnoobie.bootloader.file=usnoobie_atmega328p_12mhz.hex +usnoobie.bootloader.unlock_bits=0x3F +usnoobie.bootloader.lock_bits=0x0F +usnoobie.build.mcu=atmega328p +usnoobie.build.f_cpu=12000000L +usnoobie.build.core=arduino +usnoobie.build.variant=standard + +############################################################## + +uno.name=Arduino Uno +uno.upload.protocol=arduino +uno.upload.maximum_size=32256 +uno.upload.speed=115200 +uno.bootloader.low_fuses=0xff +uno.bootloader.high_fuses=0xde +uno.bootloader.extended_fuses=0x05 +uno.bootloader.path=optiboot +uno.bootloader.file=optiboot_atmega328.hex +uno.bootloader.unlock_bits=0x3F +uno.bootloader.lock_bits=0x0F +uno.build.mcu=atmega328p +uno.build.f_cpu=16000000L +uno.build.core=arduino +uno.build.variant=standard + +############################################################## + +atmega328.name=Arduino Duemilanove w/ ATmega328 + +atmega328.upload.protocol=arduino +atmega328.upload.maximum_size=30720 +atmega328.upload.speed=57600 + +atmega328.bootloader.low_fuses=0xFF +atmega328.bootloader.high_fuses=0xDA +atmega328.bootloader.extended_fuses=0x05 +atmega328.bootloader.path=atmega +atmega328.bootloader.file=ATmegaBOOT_168_atmega328.hex +atmega328.bootloader.unlock_bits=0x3F +atmega328.bootloader.lock_bits=0x0F + +atmega328.build.mcu=atmega328p +atmega328.build.f_cpu=16000000L +atmega328.build.core=arduino +atmega328.build.variant=standard + +############################################################## + +diecimila.name=Arduino Diecimila or Duemilanove w/ ATmega168 + +diecimila.upload.protocol=arduino +diecimila.upload.maximum_size=14336 +diecimila.upload.speed=19200 + +diecimila.bootloader.low_fuses=0xff +diecimila.bootloader.high_fuses=0xdd +diecimila.bootloader.extended_fuses=0x00 +diecimila.bootloader.path=atmega +diecimila.bootloader.file=ATmegaBOOT_168_diecimila.hex +diecimila.bootloader.unlock_bits=0x3F +diecimila.bootloader.lock_bits=0x0F + +diecimila.build.mcu=atmega168 +diecimila.build.f_cpu=16000000L +diecimila.build.core=arduino +diecimila.build.variant=standard + +############################################################## + +nano328.name=Arduino Nano w/ ATmega328 + +nano328.upload.protocol=arduino +nano328.upload.maximum_size=30720 +nano328.upload.speed=57600 + +nano328.bootloader.low_fuses=0xFF +nano328.bootloader.high_fuses=0xDA +nano328.bootloader.extended_fuses=0x05 +nano328.bootloader.path=atmega +nano328.bootloader.file=ATmegaBOOT_168_atmega328.hex +nano328.bootloader.unlock_bits=0x3F +nano328.bootloader.lock_bits=0x0F + +nano328.build.mcu=atmega328p +nano328.build.f_cpu=16000000L +nano328.build.core=arduino +nano328.build.variant=eightanaloginputs + +############################################################## + +nano.name=Arduino Nano w/ ATmega168 + +nano.upload.protocol=arduino +nano.upload.maximum_size=14336 +nano.upload.speed=19200 + +nano.bootloader.low_fuses=0xff +nano.bootloader.high_fuses=0xdd +nano.bootloader.extended_fuses=0x00 +nano.bootloader.path=atmega +nano.bootloader.file=ATmegaBOOT_168_diecimila.hex +nano.bootloader.unlock_bits=0x3F +nano.bootloader.lock_bits=0x0F + +nano.build.mcu=atmega168 +nano.build.f_cpu=16000000L +nano.build.core=arduino +nano.build.variant=eightanaloginputs + +############################################################## + +mega2560.name=Arduino Mega 2560 or Mega ADK + +mega2560.upload.protocol=wiring +mega2560.upload.maximum_size=258048 +mega2560.upload.speed=115200 + +mega2560.bootloader.low_fuses=0xFF +mega2560.bootloader.high_fuses=0xD8 +mega2560.bootloader.extended_fuses=0xFD +mega2560.bootloader.path=stk500v2 +mega2560.bootloader.file=stk500boot_v2_mega2560.hex +mega2560.bootloader.unlock_bits=0x3F +mega2560.bootloader.lock_bits=0x0F + +mega2560.build.mcu=atmega2560 +mega2560.build.f_cpu=16000000L +mega2560.build.core=arduino +mega2560.build.variant=mega + +############################################################## + +mega.name=Arduino Mega (ATmega1280) + +mega.upload.protocol=arduino +mega.upload.maximum_size=126976 +mega.upload.speed=57600 + +mega.bootloader.low_fuses=0xFF +mega.bootloader.high_fuses=0xDA +mega.bootloader.extended_fuses=0xF5 +mega.bootloader.path=atmega +mega.bootloader.file=ATmegaBOOT_168_atmega1280.hex +mega.bootloader.unlock_bits=0x3F +mega.bootloader.lock_bits=0x0F + +mega.build.mcu=atmega1280 +mega.build.f_cpu=16000000L +mega.build.core=arduino +mega.build.variant=mega + +############################################################## + +leonardo.name=Arduino Leonardo +leonardo.upload.protocol=avr109 +leonardo.upload.maximum_size=28672 +leonardo.upload.speed=57600 +leonardo.upload.disable_flushing=true +leonardo.bootloader.low_fuses=0xff +leonardo.bootloader.high_fuses=0xd8 +leonardo.bootloader.extended_fuses=0xcb +leonardo.bootloader.path=caterina +leonardo.bootloader.file=Caterina-Leonardo.hex +leonardo.bootloader.unlock_bits=0x3F +leonardo.bootloader.lock_bits=0x2F +leonardo.build.mcu=atmega32u4 +leonardo.build.f_cpu=16000000L +leonardo.build.vid=0x2341 +leonardo.build.pid=0x8036 +leonardo.build.core=arduino +leonardo.build.variant=leonardo + +############################################################## + +esplora.name=Arduino Esplora +esplora.upload.protocol=avr109 +esplora.upload.maximum_size=28672 +esplora.upload.speed=57600 +esplora.upload.disable_flushing=true +esplora.bootloader.low_fuses=0xff +esplora.bootloader.high_fuses=0xd8 +esplora.bootloader.extended_fuses=0xcb +esplora.bootloader.path=caterina +esplora.bootloader.file=Caterina-Esplora.hex +esplora.bootloader.unlock_bits=0x3F +esplora.bootloader.lock_bits=0x2F +esplora.build.mcu=atmega32u4 +esplora.build.f_cpu=16000000L +esplora.build.vid=0x2341 +esplora.build.pid=0x803C +esplora.build.core=arduino +esplora.build.variant=leonardo + +############################################################## + +micro.name=Arduino Micro +micro.upload.protocol=avr109 +micro.upload.maximum_size=28672 +micro.upload.speed=57600 +micro.upload.disable_flushing=true +micro.bootloader.low_fuses=0xff +micro.bootloader.high_fuses=0xd8 +micro.bootloader.extended_fuses=0xcb +micro.bootloader.path=caterina +micro.bootloader.file=Caterina-Micro.hex +micro.bootloader.unlock_bits=0x3F +micro.bootloader.lock_bits=0x2F +micro.build.mcu=atmega32u4 +micro.build.f_cpu=16000000L +micro.build.vid=0x2341 +micro.build.pid=0x8037 +micro.build.core=arduino +micro.build.variant=micro + +############################################################## + +mini328.name=Arduino Mini w/ ATmega328 + +mini328.upload.protocol=arduino +mini328.upload.maximum_size=28672 +mini328.upload.speed=115200 + +mini328.bootloader.low_fuses=0xff +mini328.bootloader.high_fuses=0xd8 +mini328.bootloader.extended_fuses=0x05 +mini328.bootloader.path=optiboot +mini328.bootloader.file=optiboot_atmega328-Mini.hex +mini328.bootloader.unlock_bits=0x3F +mini328.bootloader.lock_bits=0x0F + +mini328.build.mcu=atmega328p +mini328.build.f_cpu=16000000L +mini328.build.core=arduino +mini328.build.variant=eightanaloginputs + +############################################################## + +mini.name=Arduino Mini w/ ATmega168 + +mini.upload.protocol=arduino +mini.upload.maximum_size=14336 +mini.upload.speed=19200 + +mini.bootloader.low_fuses=0xff +mini.bootloader.high_fuses=0xdd +mini.bootloader.extended_fuses=0x00 +mini.bootloader.path=atmega +mini.bootloader.file=ATmegaBOOT_168_ng.hex +mini.bootloader.unlock_bits=0x3F +mini.bootloader.lock_bits=0x0F + +mini.build.mcu=atmega168 +mini.build.f_cpu=16000000L +mini.build.core=arduino +mini.build.variant=eightanaloginputs + +############################################################## + +ethernet.name=Arduino Ethernet + +ethernet.upload.protocol=arduino +ethernet.upload.maximum_size=32256 +ethernet.upload.speed=115200 + +ethernet.bootloader.low_fuses=0xff +ethernet.bootloader.high_fuses=0xde +ethernet.bootloader.extended_fuses=0x05 +ethernet.bootloader.path=optiboot +ethernet.bootloader.file=optiboot_atmega328.hex +ethernet.bootloader.unlock_bits=0x3F +ethernet.bootloader.lock_bits=0x0F + +ethernet.build.variant=standard +ethernet.build.mcu=atmega328p +ethernet.build.f_cpu=16000000L +ethernet.build.core=arduino + +############################################################## + +fio.name=Arduino Fio + +fio.upload.protocol=arduino +fio.upload.maximum_size=30720 +fio.upload.speed=57600 + +fio.bootloader.low_fuses=0xFF +fio.bootloader.high_fuses=0xDA +fio.bootloader.extended_fuses=0x05 +fio.bootloader.path=arduino:atmega +fio.bootloader.file=ATmegaBOOT_168_atmega328_pro_8MHz.hex +fio.bootloader.unlock_bits=0x3F +fio.bootloader.lock_bits=0x0F + +fio.build.mcu=atmega328p +fio.build.f_cpu=8000000L +fio.build.core=arduino +fio.build.variant=eightanaloginputs + +############################################################## + +bt328.name=Arduino BT w/ ATmega328 + +bt328.upload.protocol=arduino +bt328.upload.maximum_size=28672 +bt328.upload.speed=19200 +bt328.upload.disable_flushing=true + +bt328.bootloader.low_fuses=0xff +bt328.bootloader.high_fuses=0xd8 +bt328.bootloader.extended_fuses=0x05 +bt328.bootloader.path=bt +bt328.bootloader.file=ATmegaBOOT_168_atmega328_bt.hex +bt328.bootloader.unlock_bits=0x3F +bt328.bootloader.lock_bits=0x0F + +bt328.build.mcu=atmega328p +bt328.build.f_cpu=16000000L +bt328.build.core=arduino +bt328.build.variant=eightanaloginputs + +############################################################## + +bt.name=Arduino BT w/ ATmega168 + +bt.upload.protocol=arduino +bt.upload.maximum_size=14336 +bt.upload.speed=19200 +bt.upload.disable_flushing=true + +bt.bootloader.low_fuses=0xff +bt.bootloader.high_fuses=0xdd +bt.bootloader.extended_fuses=0x00 +bt.bootloader.path=bt +bt.bootloader.file=ATmegaBOOT_168.hex +bt.bootloader.unlock_bits=0x3F +bt.bootloader.lock_bits=0x0F + +bt.build.mcu=atmega168 +bt.build.f_cpu=16000000L +bt.build.core=arduino +bt.build.variant=eightanaloginputs + +############################################################## + +LilyPadUSB.name=LilyPad Arduino USB +LilyPadUSB.upload.protocol=avr109 +LilyPadUSB.upload.maximum_size=28672 +LilyPadUSB.upload.speed=57600 +LilyPadUSB.upload.disable_flushing=true +LilyPadUSB.bootloader.low_fuses=0xff +LilyPadUSB.bootloader.high_fuses=0xd8 +LilyPadUSB.bootloader.extended_fuses=0xce +LilyPadUSB.bootloader.path=caterina-LilyPadUSB +LilyPadUSB.bootloader.file=Caterina-LilyPadUSB.hex +LilyPadUSB.bootloader.unlock_bits=0x3F +LilyPadUSB.bootloader.lock_bits=0x2F +LilyPadUSB.build.mcu=atmega32u4 +LilyPadUSB.build.f_cpu=8000000L +LilyPadUSB.build.vid=0x1B4F +LilyPadUSB.build.pid=0x9208 +LilyPadUSB.build.core=arduino +LilyPadUSB.build.variant=leonardo + +############################################################## + +lilypad328.name=LilyPad Arduino w/ ATmega328 + +lilypad328.upload.protocol=arduino +lilypad328.upload.maximum_size=30720 +lilypad328.upload.speed=57600 + +lilypad328.bootloader.low_fuses=0xFF +lilypad328.bootloader.high_fuses=0xDA +lilypad328.bootloader.extended_fuses=0x05 +lilypad328.bootloader.path=atmega +lilypad328.bootloader.file=ATmegaBOOT_168_atmega328_pro_8MHz.hex +lilypad328.bootloader.unlock_bits=0x3F +lilypad328.bootloader.lock_bits=0x0F + +lilypad328.build.mcu=atmega328p +lilypad328.build.f_cpu=8000000L +lilypad328.build.core=arduino +lilypad328.build.variant=standard + +############################################################## + +lilypad.name=LilyPad Arduino w/ ATmega168 + +lilypad.upload.protocol=arduino +lilypad.upload.maximum_size=14336 +lilypad.upload.speed=19200 + +lilypad.bootloader.low_fuses=0xe2 +lilypad.bootloader.high_fuses=0xdd +lilypad.bootloader.extended_fuses=0x00 +lilypad.bootloader.path=lilypad +lilypad.bootloader.file=LilyPadBOOT_168.hex +lilypad.bootloader.unlock_bits=0x3F +lilypad.bootloader.lock_bits=0x0F + +lilypad.build.mcu=atmega168 +lilypad.build.f_cpu=8000000L +lilypad.build.core=arduino +lilypad.build.variant=standard + +############################################################## + +pro5v328.name=Arduino Pro or Pro Mini (5V, 16 MHz) w/ ATmega328 + +pro5v328.upload.protocol=arduino +pro5v328.upload.maximum_size=30720 +pro5v328.upload.speed=57600 + +pro5v328.bootloader.low_fuses=0xFF +pro5v328.bootloader.high_fuses=0xDA +pro5v328.bootloader.extended_fuses=0x05 +pro5v328.bootloader.path=atmega +pro5v328.bootloader.file=ATmegaBOOT_168_atmega328.hex +pro5v328.bootloader.unlock_bits=0x3F +pro5v328.bootloader.lock_bits=0x0F + +pro5v328.build.mcu=atmega328p +pro5v328.build.f_cpu=16000000L +pro5v328.build.core=arduino +pro5v328.build.variant=standard + +############################################################## + +pro5v.name=Arduino Pro or Pro Mini (5V, 16 MHz) w/ ATmega168 + +pro5v.upload.protocol=arduino +pro5v.upload.maximum_size=14336 +pro5v.upload.speed=19200 + +pro5v.bootloader.low_fuses=0xff +pro5v.bootloader.high_fuses=0xdd +pro5v.bootloader.extended_fuses=0x00 +pro5v.bootloader.path=atmega +pro5v.bootloader.file=ATmegaBOOT_168_diecimila.hex +pro5v.bootloader.unlock_bits=0x3F +pro5v.bootloader.lock_bits=0x0F + +pro5v.build.mcu=atmega168 +pro5v.build.f_cpu=16000000L +pro5v.build.core=arduino +pro5v.build.variant=standard + +############################################################## + +pro328.name=Arduino Pro or Pro Mini (3.3V, 8 MHz) w/ ATmega328 + +pro328.upload.protocol=arduino +pro328.upload.maximum_size=30720 +pro328.upload.speed=57600 + +pro328.bootloader.low_fuses=0xFF +pro328.bootloader.high_fuses=0xDA +pro328.bootloader.extended_fuses=0x05 +pro328.bootloader.path=atmega +pro328.bootloader.file=ATmegaBOOT_168_atmega328_pro_8MHz.hex +pro328.bootloader.unlock_bits=0x3F +pro328.bootloader.lock_bits=0x0F + +pro328.build.mcu=atmega328p +pro328.build.f_cpu=8000000L +pro328.build.core=arduino +pro328.build.variant=standard + +############################################################## + +pro.name=Arduino Pro or Pro Mini (3.3V, 8 MHz) w/ ATmega168 + +pro.upload.protocol=arduino +pro.upload.maximum_size=14336 +pro.upload.speed=19200 + +pro.bootloader.low_fuses=0xc6 +pro.bootloader.high_fuses=0xdd +pro.bootloader.extended_fuses=0x00 +pro.bootloader.path=atmega +pro.bootloader.file=ATmegaBOOT_168_pro_8MHz.hex +pro.bootloader.unlock_bits=0x3F +pro.bootloader.lock_bits=0x0F + +pro.build.mcu=atmega168 +pro.build.f_cpu=8000000L +pro.build.core=arduino +pro.build.variant=standard + +############################################################## + +atmega168.name=Arduino NG or older w/ ATmega168 + +atmega168.upload.protocol=arduino +atmega168.upload.maximum_size=14336 +atmega168.upload.speed=19200 + +atmega168.bootloader.low_fuses=0xff +atmega168.bootloader.high_fuses=0xdd +atmega168.bootloader.extended_fuses=0x00 +atmega168.bootloader.path=atmega +atmega168.bootloader.file=ATmegaBOOT_168_ng.hex +atmega168.bootloader.unlock_bits=0x3F +atmega168.bootloader.lock_bits=0x0F + +atmega168.build.mcu=atmega168 +atmega168.build.f_cpu=16000000L +atmega168.build.core=arduino +atmega168.build.variant=standard + +############################################################## + +atmega8.name=Arduino NG or older w/ ATmega8 + +atmega8.upload.protocol=arduino +atmega8.upload.maximum_size=7168 +atmega8.upload.speed=19200 + +atmega8.bootloader.low_fuses=0xdf +atmega8.bootloader.high_fuses=0xca +atmega8.bootloader.path=atmega8 +atmega8.bootloader.file=ATmegaBOOT-prod-firmware-2009-11-07.hex +atmega8.bootloader.unlock_bits=0x3F +atmega8.bootloader.lock_bits=0x0F + +atmega8.build.mcu=atmega8 +atmega8.build.f_cpu=16000000L +atmega8.build.core=arduino +atmega8.build.variant=standard + +############################################################## + +robotControl.name=Arduino Robot Control +robotControl.upload.protocol=avr109 +robotControl.upload.maximum_size=28672 +robotControl.upload.speed=57600 +robotControl.upload.disable_flushing=true +robotControl.bootloader.low_fuses=0xff +robotControl.bootloader.high_fuses=0xd8 +robotControl.bootloader.extended_fuses=0xcb +robotControl.bootloader.path=caterina-Arduino_Robot +robotControl.bootloader.file=Caterina-Robot-Control.hex +robotControl.bootloader.unlock_bits=0x3F +robotControl.bootloader.lock_bits=0x2F +robotControl.build.mcu=atmega32u4 +robotControl.build.f_cpu=16000000L +robotControl.build.vid=0x2341 +robotControl.build.pid=0x8038 +robotControl.build.core=robot +robotControl.build.variant=robot_control + +############################################################## + +robotMotor.name=Arduino Robot Motor +robotMotor.upload.protocol=avr109 +robotMotor.upload.maximum_size=28672 +robotMotor.upload.speed=57600 +robotMotor.upload.disable_flushing=true +robotMotor.bootloader.low_fuses=0xff +robotMotor.bootloader.high_fuses=0xd8 +robotMotor.bootloader.extended_fuses=0xcb +robotMotor.bootloader.path=caterina-Arduino_Robot +robotMotor.bootloader.file=Caterina-Robot-Motor.hex +robotMotor.bootloader.unlock_bits=0x3F +robotMotor.bootloader.lock_bits=0x2F +robotMotor.build.mcu=atmega32u4 +robotMotor.build.f_cpu=16000000L +robotMotor.build.vid=0x2341 +robotMotor.build.pid=0x8039 +robotMotor.build.core=robot +robotMotor.build.variant=robot_motor + diff --git a/arduino code/libraries/HIDSerial/Changelog.txt b/arduino code/libraries/HIDSerial/Changelog.txt new file mode 100644 index 0000000..79b5215 --- /dev/null +++ b/arduino code/libraries/HIDSerial/Changelog.txt @@ -0,0 +1,329 @@ +This file documents changes in the firmware-only USB driver for atmel's AVR +microcontrollers. New entries are always appended to the end of the file. +Scroll down to the bottom to see the most recent changes. + +2005-04-01: + - Implemented endpoint 1 as interrupt-in endpoint. + - Moved all configuration options to usbconfig.h which is not part of the + driver. + - Changed interface for usbVendorSetup(). + - Fixed compatibility with ATMega8 device. + - Various minor optimizations. + +2005-04-11: + - Changed interface to application: Use usbFunctionSetup(), usbFunctionRead() + and usbFunctionWrite() now. Added configuration options to choose which + of these functions to compile in. + - Assembler module delivers receive data non-inverted now. + - Made register and bit names compatible with more AVR devices. + +2005-05-03: + - Allow address of usbRxBuf on any memory page as long as the buffer does + not cross 256 byte page boundaries. + - Better device compatibility: works with Mega88 now. + - Code optimization in debugging module. + - Documentation updates. + +2006-01-02: + - Added (free) default Vendor- and Product-IDs bought from voti.nl. + - Added USBID-License.txt file which defines the rules for using the free + shared VID/PID pair. + - Added Readme.txt to the usbdrv directory which clarifies administrative + issues. + +2006-01-25: + - Added "configured state" to become more standards compliant. + - Added "HALT" state for interrupt endpoint. + - Driver passes the "USB Command Verifier" test from usb.org now. + - Made "serial number" a configuration option. + - Minor optimizations, we now recommend compiler option "-Os" for best + results. + - Added a version number to usbdrv.h + +2006-02-03: + - New configuration variable USB_BUFFER_SECTION for the memory section where + the USB rx buffer will go. This defaults to ".bss" if not defined. Since + this buffer MUST NOT cross 256 byte pages (not even touch a page at the + end), the user may want to pass a linker option similar to + "-Wl,--section-start=.mybuffer=0x800060". + - Provide structure for usbRequest_t. + - New defines for USB constants. + - Prepared for HID implementations. + - Increased data size limit for interrupt transfers to 8 bytes. + - New macro usbInterruptIsReady() to query interrupt buffer state. + +2006-02-18: + - Ensure that the data token which is sent as an ack to an OUT transfer is + always zero sized. This fixes a bug where the host reports an error after + sending an out transfer to the device, although all data arrived at the + device. + - Updated docs in usbdrv.h to reflect changed API in usbFunctionWrite(). + +* Release 2006-02-20 + + - Give a compiler warning when compiling with debugging turned on. + - Added Oleg Semyonov's changes for IAR-cc compatibility. + - Added new (optional) functions usbDeviceConnect() and usbDeviceDisconnect() + (also thanks to Oleg!). + - Rearranged tests in usbPoll() to save a couple of instructions in the most + likely case that no actions are pending. + - We need a delay between the SET ADDRESS request until the new address + becomes active. This delay was handled in usbPoll() until now. Since the + spec says that the delay must not exceed 2ms, previous versions required + aggressive polling during the enumeration phase. We have now moved the + handling of the delay into the interrupt routine. + - We must not reply with NAK to a SETUP transaction. We can only achieve this + by making sure that the rx buffer is empty when SETUP tokens are expected. + We therefore don't pass zero sized data packets from the status phase of + a transfer to usbPoll(). This change MAY cause troubles if you rely on + receiving a less than 8 bytes long packet in usbFunctionWrite() to + identify the end of a transfer. usbFunctionWrite() will NEVER be called + with a zero length. + +* Release 2006-03-14 + + - Improved IAR C support: tiny memory model, more devices + - Added template usbconfig.h file under the name usbconfig-prototype.h + +* Release 2006-03-26 + + - Added provision for one more interrupt-in endpoint (endpoint 3). + - Added provision for one interrupt-out endpoint (endpoint 1). + - Added flowcontrol macros for USB. + - Added provision for custom configuration descriptor. + - Allow ANY two port bits for D+ and D-. + - Merged (optional) receive endpoint number into global usbRxToken variable. + - Use USB_CFG_IOPORTNAME instead of USB_CFG_IOPORT. We now construct the + variable name from the single port letter instead of computing the address + of related ports from the output-port address. + +* Release 2006-06-26 + + - Updated documentation in usbdrv.h and usbconfig-prototype.h to reflect the + new features. + - Removed "#warning" directives because IAR does not understand them. Use + unused static variables instead to generate a warning. + - Do not include when compiling with IAR. + - Introduced USB_CFG_DESCR_PROPS_* in usbconfig.h to configure how each + USB descriptor should be handled. It is now possible to provide descriptor + data in Flash, RAM or dynamically at runtime. + - STALL is now a status in usbTxLen* instead of a message. We can now conform + to the spec and leave the stall status pending until it is cleared. + - Made usbTxPacketCnt1 and usbTxPacketCnt3 public. This allows the + application code to reset data toggling on interrupt pipes. + +* Release 2006-07-18 + + - Added an #if !defined __ASSEMBLER__ to the warning in usbdrv.h. This fixes + an assembler error. + - usbDeviceDisconnect() takes pull-up resistor to high impedance now. + +* Release 2007-02-01 + + - Merged in some code size improvements from usbtiny (thanks to Dick + Streefland for these optimizations!) + - Special alignment requirement for usbRxBuf not required any more. Thanks + again to Dick Streefland for this hint! + - Reverted to "#warning" instead of unused static variables -- new versions + of IAR CC should handle this directive. + - Changed Open Source license to GNU GPL v2 in order to make linking against + other free libraries easier. We no longer require publication of the + circuit diagrams, but we STRONGLY encourage it. If you improve the driver + itself, PLEASE grant us a royalty free license to your changes for our + commercial license. + +* Release 2007-03-29 + + - New configuration option "USB_PUBLIC" in usbconfig.h. + - Set USB version number to 1.10 instead of 1.01. + - Code used USB_CFG_DESCR_PROPS_STRING_DEVICE and + USB_CFG_DESCR_PROPS_STRING_PRODUCT inconsistently. Changed all occurrences + to USB_CFG_DESCR_PROPS_STRING_PRODUCT. + - New assembler module for 16.5 MHz RC oscillator clock with PLL in receiver + code. + - New assembler module for 16 MHz crystal. + - usbdrvasm.S contains common code only, clock-specific parts have been moved + to usbdrvasm12.S, usbdrvasm16.S and usbdrvasm165.S respectively. + +* Release 2007-06-25 + + - 16 MHz module: Do SE0 check in stuffed bits as well. + +* Release 2007-07-07 + + - Define hi8(x) for IAR compiler to limit result to 8 bits. This is necessary + for negative values. + - Added 15 MHz module contributed by V. Bosch. + - Interrupt vector name can now be configured. This is useful if somebody + wants to use a different hardware interrupt than INT0. + +* Release 2007-08-07 + + - Moved handleIn3 routine in usbdrvasm16.S so that relative jump range is + not exceeded. + - More config options: USB_RX_USER_HOOK(), USB_INITIAL_DATATOKEN, + USB_COUNT_SOF + - USB_INTR_PENDING can now be a memory address, not just I/O + +* Release 2007-09-19 + + - Split out common parts of assembler modules into separate include file + - Made endpoint numbers configurable so that given interface definitions + can be matched. See USB_CFG_EP3_NUMBER in usbconfig-prototype.h. + - Store endpoint number for interrupt/bulk-out so that usbFunctionWriteOut() + can handle any number of endpoints. + - Define usbDeviceConnect() and usbDeviceDisconnect() even if no + USB_CFG_PULLUP_IOPORTNAME is defined. Directly set D+ and D- to 0 in this + case. + +* Release 2007-12-01 + + - Optimize usbDeviceConnect() and usbDeviceDisconnect() for less code size + when USB_CFG_PULLUP_IOPORTNAME is not defined. + +* Release 2007-12-13 + + - Renamed all include-only assembler modules from *.S to *.inc so that + people don't add them to their project sources. + - Distribute leap bits in tx loop more evenly for 16 MHz module. + - Use "macro" and "endm" instead of ".macro" and ".endm" for IAR + - Avoid compiler warnings for constant expr range by casting some values in + USB descriptors. + +* Release 2008-01-21 + + - Fixed bug in 15 and 16 MHz module where the new address set with + SET_ADDRESS was already accepted at the next NAK or ACK we send, not at + the next data packet we send. This caused problems when the host polled + too fast. Thanks to Alexander Neumann for his help and patience debugging + this issue! + +* Release 2008-02-05 + + - Fixed bug in 16.5 MHz module where a register was used in the interrupt + handler before it was pushed. This bug was introduced with version + 2007-09-19 when common parts were moved to a separate file. + - Optimized CRC routine (thanks to Reimar Doeffinger). + +* Release 2008-02-16 + + - Removed outdated IAR compatibility stuff (code sections). + - Added hook macros for USB_RESET_HOOK() and USB_SET_ADDRESS_HOOK(). + - Added optional routine usbMeasureFrameLength() for calibration of the + internal RC oscillator. + +* Release 2008-02-28 + + - USB_INITIAL_DATATOKEN defaults to USBPID_DATA1 now, which means that we + start with sending USBPID_DATA0. + - Changed defaults in usbconfig-prototype.h + - Added free USB VID/PID pair for MIDI class devices + - Restructured AVR-USB as separate package, not part of PowerSwitch any more. + +* Release 2008-04-18 + + - Restructured usbdrv.c so that it is easier to read and understand. + - Better code optimization with gcc 4. + - If a second interrupt in endpoint is enabled, also add it to config + descriptor. + - Added config option for long transfers (above 254 bytes), see + USB_CFG_LONG_TRANSFERS in usbconfig.h. + - Added 20 MHz module contributed by Jeroen Benschop. + +* Release 2008-05-13 + + - Fixed bug in libs-host/hiddata.c function usbhidGetReport(): length + was not incremented, pointer to length was incremented instead. + - Added code to command line tool(s) which claims an interface. This code + is disabled by default, but may be necessary on newer Linux kernels. + - Added usbconfig.h option "USB_CFG_CHECK_DATA_TOGGLING". + - New header "usbportability.h" prepares ports to other development + environments. + - Long transfers (above 254 bytes) did not work when usbFunctionRead() was + used to supply the data. Fixed this bug. [Thanks to Alexander Neumann!] + - In hiddata.c (example code for sending/receiving data over HID), use + USB_RECIP_DEVICE instead of USB_RECIP_INTERFACE for control transfers so + that we need not claim the interface. + - in usbPoll() loop 20 times polling for RESET state instead of 10 times. + This accounts for the higher clock rates we now support. + - Added a module for 12.8 MHz RC oscillator with PLL in receiver loop. + - Added hook to SOF code so that oscillator can be tuned to USB frame clock. + - Added timeout to waitForJ loop. Helps preventing unexpected hangs. + - Added example code for oscillator tuning to libs-device (thanks to + Henrik Haftmann for the idea to this routine). + - Implemented option USB_CFG_SUPPRESS_INTR_CODE. + +* Release 2008-10-22 + + - Fixed libs-device/osctune.h: OSCCAL is memory address on ATMega88 and + similar, not offset of 0x20 needs to be added. + - Allow distribution under GPLv3 for those who have to link against other + code distributed under GPLv3. + +* Release 2008-11-26 + + - Removed libusb-win32 dependency for hid-data example in Makefile.windows. + It was never required and confused many people. + - Added extern uchar usbRxToken to usbdrv.h. + - Integrated a module with CRC checks at 18 MHz by Lukas Schrittwieser. + +* Release 2009-03-23 + + - Hid-mouse example used settings from hid-data example, fixed that. + - Renamed project to V-USB due to a trademark issue with Atmel(r). + - Changed CommercialLicense.txt and USBID-License.txt to make the + background of USB ID registration clearer. + +* Release 2009-04-15 + + - Changed CommercialLicense.txt to reflect the new range of PIDs from + Jason Kotzin. + - Removed USBID-License.txt in favor of USB-IDs-for-free.txt and + USB-ID-FAQ.txt + - Fixed a bug in the 12.8 MHz module: End Of Packet decection was made in + the center between bit 0 and 1 of each byte. This is where the data lines + are expected to change and the sampled data may therefore be nonsense. + We therefore check EOP ONLY if bits 0 AND 1 have both been read as 0 on D-. + - Fixed a bitstuffing problem in the 16 MHz module: If bit 6 was stuffed, + the unstuffing code in the receiver routine was 1 cycle too long. If + multiple bytes had the unstuffing in bit 6, the error summed up until the + receiver was out of sync. + - Included option for faster CRC routine. + Thanks to Slawomir Fras (BoskiDialer) for this code! + - Updated bits in Configuration Descriptor's bmAttributes according to + USB 1.1 (in particular bit 7, it is a must-be-set bit now). + +* Release 2009-08-22 + + - Moved first DBG1() after odDebugInit() in all examples. + - Use vector INT0_vect instead of SIG_INTERRUPT0 if defined. This makes + V-USB compatible with the new "p" suffix devices (e.g. ATMega328p). + - USB_CFG_CLOCK_KHZ setting is now required in usbconfig.h (no default any + more). + - New option USB_CFG_DRIVER_FLASH_PAGE allows boot loaders on devices with + more than 64 kB flash. + - Built-in configuration descriptor allows custom definition for second + endpoint now. + +* Release 2010-07-15 + + - Fixed bug in usbDriverSetup() which prevented descriptor sizes above 255 + bytes. + - Avoid a compiler warning for unused parameter in usbHandleResetHook() when + compiler option -Wextra is enabled. + - Fixed wrong hex value for some IDs in USB-IDs-for-free.txt. + - Keep a define for USBATTR_BUSPOWER, although the flag does not exist + in USB 1.1 any more. Set it to 0. This is for backward compatibility. + +* Release 2012-01-09 + + - Define a separate (defined) type for usbMsgPtr so that projects using a + tiny memory model can define it to an 8 bit type in usbconfig.h. This + change also saves a couple of bytes when using a scalar 16 bit type. + - Inserted "const" keyword for all PROGMEM declarations because new GCC + requires it. + - Fixed problem with dependence of usbportability.h on usbconfig.h. This + problem occurred with IAR CC only. + - Prepared repository for github.com. + +* Release 2012-12-06 \ No newline at end of file diff --git a/arduino code/libraries/HIDSerial/CommercialLicense.txt b/arduino code/libraries/HIDSerial/CommercialLicense.txt new file mode 100644 index 0000000..de1a2b0 --- /dev/null +++ b/arduino code/libraries/HIDSerial/CommercialLicense.txt @@ -0,0 +1,166 @@ +V-USB Driver Software License Agreement +Version 2012-07-09 + +THIS LICENSE AGREEMENT GRANTS YOU CERTAIN RIGHTS IN A SOFTWARE. YOU CAN +ENTER INTO THIS AGREEMENT AND ACQUIRE THE RIGHTS OUTLINED BELOW BY PAYING +THE AMOUNT ACCORDING TO SECTION 4 ("PAYMENT") TO OBJECTIVE DEVELOPMENT. + + +1 DEFINITIONS + +1.1 "OBJECTIVE DEVELOPMENT" shall mean OBJECTIVE DEVELOPMENT Software GmbH, +Grosse Schiffgasse 1A/7, 1020 Wien, AUSTRIA. + +1.2 "You" shall mean the Licensee. + +1.3 "V-USB" shall mean all files included in the package distributed under +the name "vusb" by OBJECTIVE DEVELOPMENT (http://www.obdev.at/vusb/) +unless otherwise noted. This includes the firmware-only USB device +implementation for Atmel AVR microcontrollers, some simple device examples +and host side software examples and libraries. + + +2 LICENSE GRANTS + +2.1 Source Code. OBJECTIVE DEVELOPMENT shall furnish you with the source +code of V-USB. + +2.2 Distribution and Use. OBJECTIVE DEVELOPMENT grants you the +non-exclusive right to use, copy and distribute V-USB with your hardware +product(s), restricted by the limitations in section 3 below. + +2.3 Modifications. OBJECTIVE DEVELOPMENT grants you the right to modify +the source code and your copy of V-USB according to your needs. + +2.4 USB IDs. OBJECTIVE DEVELOPMENT furnishes you with one or two USB +Product ID(s), sent to you in e-mail. These Product IDs are reserved +exclusively for you. OBJECTIVE DEVELOPMENT has obtained USB Product ID +ranges under the Vendor ID 5824 from Wouter van Ooijen (Van Ooijen +Technische Informatica, www.voti.nl) and under the Vendor ID 8352 from +Jason Kotzin (now flirc.tv, Inc.). Both owners of the Vendor IDs have +obtained these IDs from the USB Implementers Forum, Inc. (www.usb.org). +OBJECTIVE DEVELOPMENT disclaims all liability which might arise from the +assignment of USB IDs. + +2.5 USB Certification. Although not part of this agreement, we want to make +it clear that you cannot become USB certified when you use V-USB or a USB +Product ID assigned by OBJECTIVE DEVELOPMENT. AVR microcontrollers don't +meet the electrical specifications required by the USB specification and +the USB Implementers Forum certifies only members who bought a Vendor ID of +their own. + + +3 LICENSE RESTRICTIONS + +3.1 Number of Units. Only one of the following three definitions is +applicable. Which one is determined by the amount you pay to OBJECTIVE +DEVELOPMENT, see section 4 ("Payment") below. + +Hobby License: You may use V-USB according to section 2 above in no more +than 5 hardware units. These units must not be sold for profit. + +Entry Level License: You may use V-USB according to section 2 above in no +more than 150 hardware units. + +Professional License: You may use V-USB according to section 2 above in +any number of hardware units, except for large scale production ("unlimited +fair use"). Quantities below 10,000 units are not considered large scale +production. If your reach quantities which are obviously large scale +production, you must pay a license fee of 0.10 EUR per unit for all units +above 10,000. + +3.2 Rental. You may not rent, lease, or lend V-USB or otherwise encumber +any copy of V-USB, or any of the rights granted herein. + +3.3 Transfer. You may not transfer your rights under this Agreement to +another party without OBJECTIVE DEVELOPMENT's prior written consent. If +such consent is obtained, you may permanently transfer this License to +another party. The recipient of such transfer must agree to all terms and +conditions of this Agreement. + +3.4 Reservation of Rights. OBJECTIVE DEVELOPMENT retains all rights not +expressly granted. + +3.5 Non-Exclusive Rights. Your license rights under this Agreement are +non-exclusive. + +3.6 Third Party Rights. This Agreement cannot grant you rights controlled +by third parties. In particular, you are not allowed to use the USB logo or +other trademarks owned by the USB Implementers Forum, Inc. without their +consent. Since such consent depends on USB certification, it should be +noted that V-USB will not pass certification because it does not +implement checksum verification and the microcontroller ports do not meet +the electrical specifications. + + +4 PAYMENT + +The payment amount depends on the variation of this agreement (according to +section 3.1) into which you want to enter. Concrete prices are listed on +OBJECTIVE DEVELOPMENT's web site, usually at +http://www.obdev.at/vusb/license.html. You agree to pay the amount listed +there to OBJECTIVE DEVELOPMENT or OBJECTIVE DEVELOPMENT's payment processor +or reseller. + + +5 COPYRIGHT AND OWNERSHIP + +V-USB is protected by copyright laws and international copyright +treaties, as well as other intellectual property laws and treaties. V-USB +is licensed, not sold. + + +6 TERM AND TERMINATION + +6.1 Term. This Agreement shall continue indefinitely. However, OBJECTIVE +DEVELOPMENT may terminate this Agreement and revoke the granted license and +USB-IDs if you fail to comply with any of its terms and conditions. + +6.2 Survival of Terms. All provisions regarding secrecy, confidentiality +and limitation of liability shall survive termination of this agreement. + + +7 DISCLAIMER OF WARRANTY AND LIABILITY + +LIMITED WARRANTY. V-USB IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, OBJECTIVE +DEVELOPMENT AND ITS SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND +NON-INFRINGEMENT, WITH REGARD TO V-USB, AND THE PROVISION OF OR FAILURE +TO PROVIDE SUPPORT SERVICES. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL +RIGHTS. YOU MAY HAVE OTHERS, WHICH VARY FROM STATE/JURISDICTION TO +STATE/JURISDICTION. + +LIMITATION OF LIABILITY. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, +IN NO EVENT SHALL OBJECTIVE DEVELOPMENT OR ITS SUPPLIERS BE LIABLE FOR ANY +SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER +(INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, +BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY +LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE V-USB OR THE +PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF OBJECTIVE +DEVELOPMENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY +CASE, OBJECTIVE DEVELOPMENT'S ENTIRE LIABILITY UNDER ANY PROVISION OF THIS +AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR V-USB. + + +8 MISCELLANEOUS TERMS + +8.1 Marketing. OBJECTIVE DEVELOPMENT has the right to mention for marketing +purposes that you entered into this agreement. + +8.2 Entire Agreement. This document represents the entire agreement between +OBJECTIVE DEVELOPMENT and you. It may only be modified in writing signed by +an authorized representative of both, OBJECTIVE DEVELOPMENT and you. + +8.3 Severability. In case a provision of these terms and conditions should +be or become partly or entirely invalid, ineffective, or not executable, +the validity of all other provisions shall not be affected. + +8.4 Applicable Law. This agreement is governed by the laws of the Republic +of Austria. + +8.5 Responsible Courts. The responsible courts in Vienna/Austria will have +exclusive jurisdiction regarding all disputes in connection with this +agreement. + diff --git a/arduino code/libraries/HIDSerial/HIDSerial.cpp b/arduino code/libraries/HIDSerial/HIDSerial.cpp new file mode 100644 index 0000000..afb95ea --- /dev/null +++ b/arduino code/libraries/HIDSerial/HIDSerial.cpp @@ -0,0 +1,178 @@ +#include "HIDSerial.h" + +#include +#include +#include /* for sei() */ +#include /* for _delay_ms() */ +#include +#include /* required by usbdrv.h */ +#include "usbdrv.h" + +#include +#include +#include + +static uchar received = 0; +static uchar outBuffer[8]; +static uchar inBuffer[HIDSERIAL_INBUFFER_SIZE]; +static uchar reportId = 0; +static uchar bytesRemaining; +static uchar* pos; + +PROGMEM const char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { /* USB report descriptor */ + 0x06, 0x00, 0xff, // USAGE_PAGE (Generic Desktop) + 0x09, 0x01, // USAGE (Vendor Usage 1) + 0xa1, 0x01, // COLLECTION (Application) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x08, // REPORT_COUNT (8) + 0x09, 0x00, // USAGE (Undefined) + 0x82, 0x02, 0x01, // INPUT (Data,Var,Abs,Buf) + 0x95, HIDSERIAL_INBUFFER_SIZE, // REPORT_COUNT (32) + 0x09, 0x00, // USAGE (Undefined) + 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf) + 0xc0 // END_COLLECTION +}; + +/* usbFunctionRead() is called when the host requests a chunk of data from + * the device. For more information see the documentation in usbdrv/usbdrv.h. + */ +uchar usbFunctionRead(uchar *data, uchar len) +{ + return 0; +} + +/* usbFunctionWrite() is called when the host sends a chunk of data to the + * device. For more information see the documentation in usbdrv/usbdrv.h. + */ +uchar usbFunctionWrite(uchar *data, uchar len) +{ + if (reportId == 0) { + int i; + if(len > bytesRemaining) + len = bytesRemaining; + bytesRemaining -= len; + //int start = (pos==inBuffer)?1:0; + for(i=0;iwValue.bytes[0]; + if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* HID class request */ + if(rq->bRequest == USBRQ_HID_GET_REPORT){ + /* wValue: ReportType (highbyte), ReportID (lowbyte) */ + /* since we have only one report type, we can ignore the report-ID */ + return USB_NO_MSG; /* use usbFunctionRead() to obtain data */ + }else if(rq->bRequest == USBRQ_HID_SET_REPORT){ + /* since we have only one report type, we can ignore the report-ID */ + pos = inBuffer; + bytesRemaining = rq->wLength.word; + if(bytesRemaining > sizeof(inBuffer)) + bytesRemaining = sizeof(inBuffer); + return USB_NO_MSG; /* use usbFunctionWrite() to receive data from host */ + } + }else{ + /* ignore vendor type requests, we don't use any */ + } + return 0; +} + +HIDSerial::HIDSerial() +{ + +} + +void HIDSerial::begin() +{ + uchar i; + cli(); + usbDeviceDisconnect(); + i = 0; + while(--i){ /* fake USB disconnect for > 250 ms */ + _delay_ms(1); + } + usbDeviceConnect(); + usbInit(); + sei(); + + received = 0; +} + +void HIDSerial::poll() +{ + usbPoll(); +} + +uchar HIDSerial::available() +{ + return received; +} + +uchar HIDSerial::read(uchar *buffer) +{ + if(received == 0) return 0; + int i; + for(i=0;inBuffer[i]!=0&&i + Copyright (C) + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/arduino code/libraries/HIDSerial/Readme.txt b/arduino code/libraries/HIDSerial/Readme.txt new file mode 100644 index 0000000..970dc66 --- /dev/null +++ b/arduino code/libraries/HIDSerial/Readme.txt @@ -0,0 +1,172 @@ +This is the Readme file to Objective Development's firmware-only USB driver +for Atmel AVR microcontrollers. For more information please visit +http://www.obdev.at/vusb/ + +This directory contains the USB firmware only. Copy it as-is to your own +project and add all .c and .S files to your project (these files are marked +with an asterisk in the list below). Then copy usbconfig-prototype.h as +usbconfig.h to your project and edit it according to your configuration. + + +TECHNICAL DOCUMENTATION +======================= +The technical documentation (API) for the firmware driver is contained in the +file "usbdrv.h". Please read all of it carefully! Configuration options are +documented in "usbconfig-prototype.h". + +The driver consists of the following files: + Readme.txt ............. The file you are currently reading. + Changelog.txt .......... Release notes for all versions of the driver. + usbdrv.h ............... Driver interface definitions and technical docs. +* usbdrv.c ............... High level language part of the driver. Link this + module to your code! +* usbdrvasm.S ............ Assembler part of the driver. This module is mostly + a stub and includes one of the usbdrvasm*.S files + depending on processor clock. Link this module to + your code! + usbdrvasm*.inc ......... Assembler routines for particular clock frequencies. + Included by usbdrvasm.S, don't link it directly! + asmcommon.inc .......... Common assembler routines. Included by + usbdrvasm*.inc, don't link it directly! + usbconfig-prototype.h .. Prototype for your own usbdrv.h file. +* oddebug.c .............. Debug functions. Only used when DEBUG_LEVEL is + defined to a value greater than 0. Link this module + to your code! + oddebug.h .............. Interface definitions of the debug module. + usbportability.h ....... Header with compiler-dependent stuff. + usbdrvasm.asm .......... Compatibility stub for IAR-C-compiler. Use this + module instead of usbdrvasm.S when you assembler + with IAR's tools. + License.txt ............ Open Source license for this driver. + CommercialLicense.txt .. Optional commercial license for this driver. + USB-ID-FAQ.txt ......... General infos about USB Product- and Vendor-IDs. + USB-IDs-for-free.txt ... List and terms of use for free shared PIDs. + +(*) ... These files should be linked to your project. + + +CPU CORE CLOCK FREQUENCY +======================== +We supply assembler modules for clock frequencies of 12 MHz, 12.8 MHz, 15 MHz, +16 MHz, 16.5 MHz 18 MHz and 20 MHz. Other clock rates are not supported. The +actual clock rate must be configured in usbconfig.h. + +12 MHz Clock +This is the traditional clock rate of V-USB because it's the lowest clock +rate where the timing constraints of the USB spec can be met. + +15 MHz Clock +Similar to 12 MHz, but some NOPs inserted. On the other hand, the higher clock +rate allows for some loops which make the resulting code size somewhat smaller +than the 12 MHz version. + +16 MHz Clock +This clock rate has been added for users of the Arduino board and other +ready-made boards which come with a fixed 16 MHz crystal. It's also an option +if you need the slightly higher clock rate for performance reasons. Since +16 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code +is somewhat tricky and has to insert a leap cycle every third byte. + +12.8 MHz and 16.5 MHz Clock +The assembler modules for these clock rates differ from the other modules +because they have been built for an RC oscillator with only 1% precision. The +receiver code inserts leap cycles to compensate for clock deviations. 1% is +also the precision which can be achieved by calibrating the internal RC +oscillator of the AVR. Please note that only AVRs with internal 64 MHz PLL +oscillator can reach 16.5 MHz with the RC oscillator. This includes the very +popular ATTiny25, ATTiny45, ATTiny85 series as well as the ATTiny26. Almost +all AVRs can reach 12.8 MHz, although this is outside the specified range. + +See the EasyLogger example at http://www.obdev.at/vusb/easylogger.html for +code which calibrates the RC oscillator based on the USB frame clock. + +18 MHz Clock +This module is closer to the USB specification because it performs an on the +fly CRC check for incoming packets. Packets with invalid checksum are +discarded as required by the spec. If you also implement checks for data +PID toggling on application level (see option USB_CFG_CHECK_DATA_TOGGLING +in usbconfig.h for more info), this ensures data integrity. Due to the CRC +tables and alignment requirements, this code is bigger than modules for other +clock rates. To activate this module, you must define USB_CFG_CHECK_CRC to 1 +and USB_CFG_CLOCK_KHZ to 18000 in usbconfig.h. + +20 MHz Clock +This module is for people who won't do it with less than the maximum. Since +20 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code +uses similar tricks as the 16 MHz module to insert leap cycles. + + +USB IDENTIFIERS +=============== +Every USB device needs a vendor- and a product-identifier (VID and PID). VIDs +are obtained from usb.org for a price of 1,500 USD. Once you have a VID, you +can assign PIDs at will. + +Since an entry level cost of 1,500 USD is too high for most small companies +and hobbyists, we provide some VID/PID pairs for free. See the file +USB-IDs-for-free.txt for details. + +Objective Development also has some license offerings which include product +IDs. See http://www.obdev.at/vusb/ for details. + + +DEVELOPMENT SYSTEM +================== +This driver has been developed and optimized for the GNU compiler version 3 +and 4. We recommend that you use the GNU compiler suite because it is freely +available. V-USB has also been ported to the IAR compiler and assembler. It +has been tested with IAR 4.10B/W32 and 4.12A/W32 on an ATmega8 with the +"small" and "tiny" memory model. Not every release is tested with IAR CC and +the driver may therefore fail to compile with IAR. Please note that gcc is +more efficient for usbdrv.c because this module has been deliberately +optimized for gcc. + +Gcc version 3 produces smaller code than version 4 due to new optimizing +capabilities which don't always improve things on 8 bit CPUs. The code size +generated by gcc 4 can be reduced with the compiler options +-fno-move-loop-invariants, -fno-tree-scev-cprop and +-fno-inline-small-functions in addition to -Os. On devices with more than +8k of flash memory, we also recommend the linker option --relax (written as +-Wl,--relax for gcc) to convert absolute calls into relative where possible. + +For more information about optimizing options see: + + http://www.tty1.net/blog/2008-04-29-avr-gcc-optimisations_en.html + +These optimizations are good for gcc 4.x. Version 3.x of gcc does not support +most of these options and produces good code anyway. + + +USING V-USB FOR FREE +==================== +The AVR firmware driver is published under the GNU General Public License +Version 2 (GPL2) and the GNU General Public License Version 3 (GPL3). It is +your choice whether you apply the terms of version 2 or version 3. + +If you decide for the free GPL2 or GPL3, we STRONGLY ENCOURAGE you to do the +following things IN ADDITION to the obligations from the GPL: + +(1) Publish your entire project on a web site and drop us a note with the URL. +Use the form at http://www.obdev.at/vusb/feedback.html for your submission. +If you don't have a web site, you can publish the project in obdev's +documentation wiki at +http://www.obdev.at/goto.php?t=vusb-wiki&p=hosted-projects. + +(2) Adhere to minimum publication standards. Please include AT LEAST: + - a circuit diagram in PDF, PNG or GIF format + - full source code for the host software + - a Readme.txt file in ASCII format which describes the purpose of the + project and what can be found in which directories and which files + - a reference to http://www.obdev.at/vusb/ + +(3) If you improve the driver firmware itself, please give us a free license +to your modifications for our commercial license offerings. + + +COMMERCIAL LICENSES FOR V-USB +============================= +If you don't want to publish your source code under the terms of the GPL, +you can simply pay money for V-USB. As an additional benefit you get +USB PIDs for free, reserved exclusively to you. See the file +"CommercialLicense.txt" for details. + diff --git a/arduino code/libraries/HIDSerial/USB-ID-FAQ.txt b/arduino code/libraries/HIDSerial/USB-ID-FAQ.txt new file mode 100644 index 0000000..a4a6bd6 --- /dev/null +++ b/arduino code/libraries/HIDSerial/USB-ID-FAQ.txt @@ -0,0 +1,149 @@ +Version 2012-07-09 + +========================== +WHY DO WE NEED THESE IDs? +========================== + +USB is more than a low level protocol for data transport. It also defines a +common set of requests which must be understood by all devices. And as part +of these common requests, the specification defines data structures, the +USB Descriptors, which are used to describe the properties of the device. + +From the perspective of an operating system, it is therefore possible to find +out basic properties of a device (such as e.g. the manufacturer and the name +of the device) without a device-specific driver. This is essential because +the operating system can choose a driver to load based on this information +(Plug-And-Play). + +Among the most important properties in the Device Descriptor are the USB +Vendor- and Product-ID. Both are 16 bit integers. The most simple form of +driver matching is based on these IDs. The driver announces the Vendor- and +Product-IDs of the devices it can handle and the operating system loads the +appropriate driver when the device is connected. + +It is obvious that this technique only works if the pair Vendor- plus +Product-ID is unique: Only devices which require the same driver can have the +same pair of IDs. + + +===================================================== +HOW DOES THE USB STANDARD ENSURE THAT IDs ARE UNIQUE? +===================================================== + +Since it is so important that USB IDs are unique, the USB Implementers Forum, +Inc. (usb.org) needs a way to enforce this legally. It is not forbidden by +law to build a device and assign it any random numbers as IDs. Usb.org +therefore needs an agreement to regulate the use of USB IDs. The agreement +binds only parties who agreed to it, of course. Everybody else is free to use +any numbers for their IDs. + +So how can usb.org ensure that every manufacturer of USB devices enters into +an agreement with them? They do it via trademark licensing. Usb.org has +registered the trademark "USB", all associated logos and related terms. If +you want to put an USB logo on your product or claim that it is USB +compliant, you must license these trademarks from usb.org. And this is where +you enter into an agreement. See the "USB-IF Trademark License Agreement and +Usage Guidelines for the USB-IF Logo" at +http://www.usb.org/developers/logo_license/. + +Licensing the USB trademarks requires that you buy a USB Vendor-ID from +usb.org (one-time fee of ca. 2,000 USD), that you become a member of usb.org +(yearly fee of ca. 4,000 USD) and that you meet all the technical +specifications from the USB spec. + +This means that most hobbyists and small companies will never be able to +become USB compliant, just because membership is so expensive. And you can't +be compliant with a driver based on V-USB anyway, because the AVR's port pins +don't meet the electrical specifications for USB. So, in principle, all +hobbyists and small companies are free to choose any random numbers for their +IDs. They have nothing to lose... + +There is one exception worth noting, though: If you use a sub-component which +implements USB, the vendor of the sub-components may guarantee USB +compliance. This might apply to some or all of FTDI's solutions. + + +======================================================================= +WHY SHOULD YOU OBTAIN USB IDs EVEN IF YOU DON'T LICENSE USB TRADEMARKS? +======================================================================= + +You have learned in the previous section that you are free to choose any +numbers for your IDs anyway. So why not do exactly this? There is still the +technical issue. If you choose IDs which are already in use by somebody else, +operating systems will load the wrong drivers and your device won't work. +Even if you choose IDs which are not currently in use, they may be in use in +the next version of the operating system or even after an automatic update. + +So what you need is a pair of Vendor- and Product-IDs for which you have the +guarantee that no USB compliant product uses them. This implies that no +operating system will ever ship with drivers responsible for these IDs. + + +============================================== +HOW DOES OBJECTIVE DEVELOPMENT HANDLE USB IDs? +============================================== + +Objective Development gives away pairs of USB-IDs with their V-USB licenses. +In order to ensure that these IDs are unique, Objective Development has an +agreement with the company/person who has bought the USB Vendor-ID from +usb.org. This agreement ensures that a range of USB Product-IDs is reserved +for assignment by Objective Development and that the owner of the Vendor-ID +won't give it to anybody else. + +This means that you have to trust three parties to ensure uniqueness of +your IDs: + + - Objective Development, that they don't give the same PID to more than + one person. + - The owner of the Vendor-ID that they don't assign PIDs from the range + assigned to Objective Development to anybody else. + - Usb.org that they don't assign the same Vendor-ID a second time. + + +================================== +WHO IS THE OWNER OF THE VENDOR-ID? +================================== + +Objective Development has obtained ranges of USB Product-IDs under two +Vendor-IDs: Under Vendor-ID 5824 from Wouter van Ooijen (Van Ooijen +Technische Informatica, www.voti.nl) and under Vendor-ID 8352 from Jason +Kotzin (now flirc.tv, Inc.). Both VID owners have received their Vendor-ID +directly from usb.org. + + +========================================================================= +CAN I USE USB-IDs FROM OBJECTIVE DEVELOPMENT WITH OTHER DRIVERS/HARDWARE? +========================================================================= + +The short answer is: Yes. All you get is a guarantee that the IDs are never +assigned to anybody else. What more do you need? + + +============================ +WHAT ABOUT SHARED ID PAIRS? +============================ + +Objective Development has reserved some PID/VID pairs for shared use. You +have no guarantee of uniqueness for them, except that no USB compliant device +uses them. In order to avoid technical problems, we must ensure that all +devices with the same pair of IDs use the same driver on kernel level. For +details, see the file USB-IDs-for-free.txt. + + +====================================================== +I HAVE HEARD THAT SUB-LICENSING OF USB-IDs IS ILLEGAL? +====================================================== + +A 16 bit integer number cannot be protected by copyright laws. It is not +sufficiently complex. And since none of the parties involved entered into the +USB-IF Trademark License Agreement, we are not bound by this agreement. So +there is no reason why it should be illegal to sub-license USB-IDs. + + +============================================= +WHO IS LIABLE IF THERE ARE INCOMPATIBILITIES? +============================================= + +Objective Development disclaims all liabilities which might arise from the +assignment of IDs. If you guarantee product features to your customers +without proper disclaimer, YOU are liable for that. diff --git a/arduino code/libraries/HIDSerial/USB-IDs-for-free.txt b/arduino code/libraries/HIDSerial/USB-IDs-for-free.txt new file mode 100644 index 0000000..d46517d --- /dev/null +++ b/arduino code/libraries/HIDSerial/USB-IDs-for-free.txt @@ -0,0 +1,154 @@ +Version 2009-08-22 + +=========================== +FREE USB-IDs FOR SHARED USE +=========================== + +Objective Development has reserved a set of USB Product-IDs for use according +to the guidelines outlined below. For more information about the concept of +USB IDs please see the file USB-ID-FAQ.txt. Objective Development guarantees +that the IDs listed below are not used by any USB compliant devices. + + +==================== +MECHANISM OF SHARING +==================== + +From a technical point of view, two different devices can share the same USB +Vendor- and Product-ID if they require the same driver on operating system +level. We make use of this fact by assigning separate IDs for various device +classes. On application layer, devices must be distinguished by their textual +name or serial number. We offer separate sets of IDs for discrimination by +textual name and for serial number. + +Examples for shared use of USB IDs are included with V-USB in the "examples" +subdirectory. + + +====================================== +IDs FOR DISCRIMINATION BY TEXTUAL NAME +====================================== + +If you use one of the IDs listed below, your device and host-side software +must conform to these rules: + +(1) The USB device MUST provide a textual representation of the manufacturer +and product identification. The manufacturer identification MUST be available +at least in USB language 0x0409 (English/US). + +(2) The textual manufacturer identification MUST contain either an Internet +domain name (e.g. "mycompany.com") registered and owned by you, or an e-mail +address under your control (e.g. "myname@gmx.net"). You can embed the domain +name or e-mail address in any string you like, e.g. "Objective Development +http://www.obdev.at/vusb/". + +(3) You are responsible for retaining ownership of the domain or e-mail +address for as long as any of your products are in use. + +(4) You may choose any string for the textual product identification, as long +as this string is unique within the scope of your textual manufacturer +identification. + +(5) Application side device look-up MUST be based on the textual manufacturer +and product identification in addition to VID/PID matching. The driver +matching MUST be a comparison of the entire strings, NOT a sub-string match. + +(6) For devices which implement a particular USB device class (e.g. HID), the +operating system's default class driver MUST be used. If an operating system +driver for Vendor Class devices is needed, this driver must be libusb or +libusb-win32 (see http://libusb.org/ and +http://libusb-win32.sourceforge.net/). + +Table if IDs for discrimination by textual name: + +PID dec (hex) | VID dec (hex) | Description of use +==============+===============+============================================ +1500 (0x05dc) | 5824 (0x16c0) | For Vendor Class devices with libusb +--------------+---------------+-------------------------------------------- +1503 (0x05df) | 5824 (0x16c0) | For generic HID class devices (which are + | | NOT mice, keyboards or joysticks) +--------------+---------------+-------------------------------------------- +1505 (0x05e1) | 5824 (0x16c0) | For CDC-ACM class devices (modems) +--------------+---------------+-------------------------------------------- +1508 (0x05e4) | 5824 (0x16c0) | For MIDI class devices +--------------+---------------+-------------------------------------------- + +Note that Windows caches the textual product- and vendor-description for +mice, keyboards and joysticks. Name-bsed discrimination is therefore not +recommended for these device classes. + + +======================================= +IDs FOR DISCRIMINATION BY SERIAL NUMBER +======================================= + +If you use one of the IDs listed below, your device and host-side software +must conform to these rules: + +(1) The USB device MUST provide a textual representation of the serial +number, unless ONLY the operating system's default class driver is used. +The serial number string MUST be available at least in USB language 0x0409 +(English/US). + +(2) The serial number MUST start with either an Internet domain name (e.g. +"mycompany.com") registered and owned by you, or an e-mail address under your +control (e.g. "myname@gmx.net"), both terminated with a colon (":") character. +You MAY append any string you like for further discrimination of your devices. + +(3) You are responsible for retaining ownership of the domain or e-mail +address for as long as any of your products are in use. + +(5) Application side device look-up MUST be based on the serial number string +in addition to VID/PID matching. The matching must start at the first +character of the serial number string and include the colon character +terminating your domain or e-mail address. It MAY stop anywhere after that. + +(6) For devices which implement a particular USB device class (e.g. HID), the +operating system's default class driver MUST be used. If an operating system +driver for Vendor Class devices is needed, this driver must be libusb or +libusb-win32 (see http://libusb.org/ and +http://libusb-win32.sourceforge.net/). + +(7) If ONLY the operating system's default class driver is used, e.g. for +mice, keyboards, joysticks, CDC or MIDI devices and no discrimination by an +application is needed, the serial number may be omitted. + + +Table if IDs for discrimination by serial number string: + +PID dec (hex) | VID dec (hex) | Description of use +===============+===============+=========================================== +10200 (0x27d8) | 5824 (0x16c0) | For Vendor Class devices with libusb +---------------+---------------+------------------------------------------- +10201 (0x27d9) | 5824 (0x16c0) | For generic HID class devices (which are + | | NOT mice, keyboards or joysticks) +---------------+---------------+------------------------------------------- +10202 (0x27da) | 5824 (0x16c0) | For USB Mice +---------------+---------------+------------------------------------------- +10203 (0x27db) | 5824 (0x16c0) | For USB Keyboards +---------------+---------------+------------------------------------------- +10204 (0x27dc) | 5824 (0x16c0) | For USB Joysticks +---------------+---------------+------------------------------------------- +10205 (0x27dd) | 5824 (0x16c0) | For CDC-ACM class devices (modems) +---------------+---------------+------------------------------------------- +10206 (0x27de) | 5824 (0x16c0) | For MIDI class devices +---------------+---------------+------------------------------------------- + + +================= +ORIGIN OF USB-IDs +================= + +OBJECTIVE DEVELOPMENT Software GmbH has obtained all VID/PID pairs listed +here from Wouter van Ooijen (see www.voti.nl) for exclusive disposition. +Wouter van Ooijen has obtained the VID from the USB Implementers Forum, Inc. +(see www.usb.org). The VID is registered for the company name "Van Ooijen +Technische Informatica". + + +========== +DISCLAIMER +========== + +OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any +problems which are caused by the shared use of these VID/PID pairs. diff --git a/arduino code/libraries/HIDSerial/asmcommon.inc b/arduino code/libraries/HIDSerial/asmcommon.inc new file mode 100644 index 0000000..d2a4f7c --- /dev/null +++ b/arduino code/libraries/HIDSerial/asmcommon.inc @@ -0,0 +1,187 @@ +/* Name: asmcommon.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2007-11-05 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file contains assembler code which is shared among the USB driver +implementations for different CPU cocks. Since the code must be inserted +in the middle of the module, it's split out into this file and #included. + +Jump destinations called from outside: + sofError: Called when no start sequence was found. + se0: Called when a package has been successfully received. + overflow: Called when receive buffer overflows. + doReturn: Called after sending data. + +Outside jump destinations used by this module: + waitForJ: Called to receive an already arriving packet. + sendAckAndReti: + sendNakAndReti: + sendCntAndReti: + usbSendAndReti: + +The following macros must be defined before this file is included: + .macro POP_STANDARD + .endm + .macro POP_RETI + .endm +*/ + +#define token x1 + +overflow: + ldi x2, 1< + +HIDSerial serial; + +unsigned long t = 0; +unsigned char buffer[32]; +unsigned char adcCh = 0; + +void setup() { + serial.begin(); +} + +void loop() { + + int value; + if (millis() > t + 2000) + { + t = millis(); + serial.write('A'); + serial.print(adcCh); + serial.print(" = "); + serial.println(analogRead(adcCh)); + } + + if(serial.available()) { + int size = serial.read(buffer); + if (size!=0) { + //serial.write((const uint8_t*)buffer, size); + if(buffer[0]=='a' || buffer[0]=='A') { + if(buffer[1]>='0'&&buffer[1]<='5') { + adcCh = buffer[1]-'0'; + serial.write('A'); + serial.print(adcCh); + serial.println(" selected."); + } + } + } + } + serial.poll(); +} + diff --git a/arduino code/libraries/HIDSerial/examples/button/button.ino b/arduino code/libraries/HIDSerial/examples/button/button.ino new file mode 100644 index 0000000..dd2af37 --- /dev/null +++ b/arduino code/libraries/HIDSerial/examples/button/button.ino @@ -0,0 +1,42 @@ +// ******************************* +// HID Serial Example +// Button press +// RAYSHOBBY.net +// +// This program opens an HID serial +// channel and shows the status of +// the pushbutton as it's pressed +// or released. To use it, you need +// to run the HIDSerialMonitor +// from a host computer. +// ******************************* + +#include + +#define BUTTON 4 +HIDSerial serial; + +void setup() { + serial.begin(); + pinMode(BUTTON, INPUT); + digitalWrite(BUTTON, HIGH); +} + +void loop() { + static int last = HIGH; + static int count = 0; + int b = digitalRead(BUTTON); + if(b!=last) { + serial.print((b==LOW) ? "button down :" : "button up."); + if (b==LOW) { + count ++; + serial.println(count); + } else { + serial.println(""); + } + last = b; + delay(50); + } + serial.poll(); +} + diff --git a/arduino code/libraries/HIDSerial/examples/echo/echo.ino b/arduino code/libraries/HIDSerial/examples/echo/echo.ino new file mode 100644 index 0000000..d791deb --- /dev/null +++ b/arduino code/libraries/HIDSerial/examples/echo/echo.ino @@ -0,0 +1,36 @@ +// ******************************* +// HID Serial Example +// Echo +// RAYSHOBBY.net +// +// This program opens an HID serial +// channel and shows how to send +// and receive messages from a host +// computer. To use it, you need +// to run the HIDSerialMonitor +// from a host computer. The program +// echos the string (up to 32 characters) +// received from the host computer. +// ******************************* + +#include + +HIDSerial serial; + +unsigned char buffer[32]; + +void setup() { + serial.begin(); +} + +void loop() { + if(serial.available()) { + int size = serial.read(buffer); + if (size!=0) { + serial.write((const uint8_t*)buffer, size); + serial.write('\n'); + } + } + serial.poll(); +} + diff --git a/arduino code/libraries/HIDSerial/examples/hello_world/hello_world.ino b/arduino code/libraries/HIDSerial/examples/hello_world/hello_world.ino new file mode 100644 index 0000000..6beff60 --- /dev/null +++ b/arduino code/libraries/HIDSerial/examples/hello_world/hello_world.ino @@ -0,0 +1,28 @@ +// ******************************* +// HID Serial Example +// Hello World +// RAYSHOBBY.net +// +// Print "Hello World" once every +// second. +// ******************************* + +#include + +HIDSerial serial; + +void setup() { + serial.begin(); +} + +unsigned long t = 0; +void loop() { + + if (t == 0 || millis() > t+1000) { + serial.println("Hello World!"); + t = millis(); + } + serial.poll(); + +} + diff --git a/arduino code/libraries/HIDSerial/oddebug.c b/arduino code/libraries/HIDSerial/oddebug.c new file mode 100644 index 0000000..19bf142 --- /dev/null +++ b/arduino code/libraries/HIDSerial/oddebug.c @@ -0,0 +1,49 @@ +/* Name: oddebug.c + * Project: AVR library + * Author: Christian Starkjohann + * Creation Date: 2005-01-16 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +#include "oddebug.h" + +#if DEBUG_LEVEL > 0 + +#warning "Never compile production devices with debugging enabled" + +static void uartPutc(char c) +{ + while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ + ODDBG_UDR = c; +} + +static uchar hexAscii(uchar h) +{ + h &= 0xf; + if(h >= 10) + h += 'a' - (uchar)10 - '0'; + h += '0'; + return h; +} + +static void printHex(uchar c) +{ + uartPutc(hexAscii(c >> 4)); + uartPutc(hexAscii(c)); +} + +void odDebug(uchar prefix, uchar *data, uchar len) +{ + printHex(prefix); + uartPutc(':'); + while(len--){ + uartPutc(' '); + printHex(*data++); + } + uartPutc('\r'); + uartPutc('\n'); +} + +#endif diff --git a/arduino code/libraries/HIDSerial/oddebug.h b/arduino code/libraries/HIDSerial/oddebug.h new file mode 100644 index 0000000..851f84d --- /dev/null +++ b/arduino code/libraries/HIDSerial/oddebug.h @@ -0,0 +1,122 @@ +/* Name: oddebug.h + * Project: AVR library + * Author: Christian Starkjohann + * Creation Date: 2005-01-16 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +#ifndef __oddebug_h_included__ +#define __oddebug_h_included__ + +/* +General Description: +This module implements a function for debug logs on the serial line of the +AVR microcontroller. Debugging can be configured with the define +'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging +calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is +2, DBG1 and DBG2 logs will be printed. + +A debug log consists of a label ('prefix') to indicate which debug log created +the output and a memory block to dump in hex ('data' and 'len'). +*/ + + +#ifndef F_CPU +# define F_CPU 12000000 /* 12 MHz */ +#endif + +/* make sure we have the UART defines: */ +#include "usbportability.h" + +#ifndef uchar +# define uchar unsigned char +#endif + +#if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ +# warning "Debugging disabled because device has no UART" +# undef DEBUG_LEVEL +#endif + +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 0 +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +# define DBG1(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG1(prefix, data, len) +#endif + +#if DEBUG_LEVEL > 1 +# define DBG2(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG2(prefix, data, len) +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +extern void odDebug(uchar prefix, uchar *data, uchar len); + +/* Try to find our control registers; ATMEL likes to rename these */ + +#if defined UBRR +# define ODDBG_UBRR UBRR +#elif defined UBRRL +# define ODDBG_UBRR UBRRL +#elif defined UBRR0 +# define ODDBG_UBRR UBRR0 +#elif defined UBRR0L +# define ODDBG_UBRR UBRR0L +#endif + +#if defined UCR +# define ODDBG_UCR UCR +#elif defined UCSRB +# define ODDBG_UCR UCSRB +#elif defined UCSR0B +# define ODDBG_UCR UCSR0B +#endif + +#if defined TXEN +# define ODDBG_TXEN TXEN +#else +# define ODDBG_TXEN TXEN0 +#endif + +#if defined USR +# define ODDBG_USR USR +#elif defined UCSRA +# define ODDBG_USR UCSRA +#elif defined UCSR0A +# define ODDBG_USR UCSR0A +#endif + +#if defined UDRE +# define ODDBG_UDRE UDRE +#else +# define ODDBG_UDRE UDRE0 +#endif + +#if defined UDR +# define ODDBG_UDR UDR +#elif defined UDR0 +# define ODDBG_UDR UDR0 +#endif + +static inline void odDebugInit(void) +{ + ODDBG_UCR |= (1<len & 0x10){ /* packet buffer was empty */ + txStatus->buffer[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ + }else{ + txStatus->len = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ + } + p = txStatus->buffer + 1; + i = len; + do{ /* if len == 0, we still copy 1 byte, but that's no problem */ + *p++ = *data++; + }while(--i > 0); /* loop control at the end is 2 bytes shorter than at beginning */ + usbCrc16Append(&txStatus->buffer[1], len); + txStatus->len = len + 4; /* len must be given including sync byte */ + DBG2(0x21 + (((int)txStatus >> 3) & 3), txStatus->buffer, len + 3); +} + +USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len) +{ + usbGenericSetInterrupt(data, len, &usbTxStatus1); +} +#endif + +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len) +{ + usbGenericSetInterrupt(data, len, &usbTxStatus3); +} +#endif +#endif /* USB_CFG_SUPPRESS_INTR_CODE */ + +/* ------------------ utilities for code following below ------------------- */ + +/* Use defines for the switch statement so that we can choose between an + * if()else if() and a switch/case based implementation. switch() is more + * efficient for a LARGE set of sequential choices, if() is better in all other + * cases. + */ +#if USB_CFG_USE_SWITCH_STATEMENT +# define SWITCH_START(cmd) switch(cmd){{ +# define SWITCH_CASE(value) }break; case (value):{ +# define SWITCH_CASE2(v1,v2) }break; case (v1): case(v2):{ +# define SWITCH_CASE3(v1,v2,v3) }break; case (v1): case(v2): case(v3):{ +# define SWITCH_DEFAULT }break; default:{ +# define SWITCH_END }} +#else +# define SWITCH_START(cmd) {uchar _cmd = cmd; if(0){ +# define SWITCH_CASE(value) }else if(_cmd == (value)){ +# define SWITCH_CASE2(v1,v2) }else if(_cmd == (v1) || _cmd == (v2)){ +# define SWITCH_CASE3(v1,v2,v3) }else if(_cmd == (v1) || _cmd == (v2) || (_cmd == v3)){ +# define SWITCH_DEFAULT }else{ +# define SWITCH_END }} +#endif + +#ifndef USB_RX_USER_HOOK +#define USB_RX_USER_HOOK(data, len) +#endif +#ifndef USB_SET_ADDRESS_HOOK +#define USB_SET_ADDRESS_HOOK() +#endif + +/* ------------------------------------------------------------------------- */ + +/* We use if() instead of #if in the macro below because #if can't be used + * in macros and the compiler optimizes constant conditions anyway. + * This may cause problems with undefined symbols if compiled without + * optimizing! + */ +#define GET_DESCRIPTOR(cfgProp, staticName) \ + if(cfgProp){ \ + if((cfgProp) & USB_PROP_IS_RAM) \ + flags = 0; \ + if((cfgProp) & USB_PROP_IS_DYNAMIC){ \ + len = usbFunctionDescriptor(rq); \ + }else{ \ + len = USB_PROP_LENGTH(cfgProp); \ + usbMsgPtr = (usbMsgPtr_t)(staticName); \ + } \ + } + +/* usbDriverDescriptor() is similar to usbFunctionDescriptor(), but used + * internally for all types of descriptors. + */ +static inline usbMsgLen_t usbDriverDescriptor(usbRequest_t *rq) +{ +usbMsgLen_t len = 0; +uchar flags = USB_FLG_MSGPTR_IS_ROM; + + SWITCH_START(rq->wValue.bytes[1]) + SWITCH_CASE(USBDESCR_DEVICE) /* 1 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) + SWITCH_CASE(USBDESCR_CONFIG) /* 2 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) + SWITCH_CASE(USBDESCR_STRING) /* 3 */ +#if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC + if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) + flags = 0; + len = usbFunctionDescriptor(rq); +#else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ + SWITCH_START(rq->wValue.bytes[0]) + SWITCH_CASE(0) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) + SWITCH_CASE(1) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) + SWITCH_CASE(2) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_PRODUCT, usbDescriptorStringDevice) + SWITCH_CASE(3) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) + SWITCH_DEFAULT + if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ + len = usbFunctionDescriptor(rq); + } + SWITCH_END +#endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ +#if USB_CFG_DESCR_PROPS_HID_REPORT /* only support HID descriptors if enabled */ + SWITCH_CASE(USBDESCR_HID) /* 0x21 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) + SWITCH_CASE(USBDESCR_HID_REPORT)/* 0x22 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) +#endif + SWITCH_DEFAULT + if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ + len = usbFunctionDescriptor(rq); + } + SWITCH_END + usbMsgFlags = flags; + return len; +} + +/* ------------------------------------------------------------------------- */ + +/* usbDriverSetup() is similar to usbFunctionSetup(), but it's used for + * standard requests instead of class and custom requests. + */ +static inline usbMsgLen_t usbDriverSetup(usbRequest_t *rq) +{ +usbMsgLen_t len = 0; +uchar *dataPtr = usbTxBuf + 9; /* there are 2 bytes free space at the end of the buffer */ +uchar value = rq->wValue.bytes[0]; +#if USB_CFG_IMPLEMENT_HALT +uchar index = rq->wIndex.bytes[0]; +#endif + + dataPtr[0] = 0; /* default reply common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ + SWITCH_START(rq->bRequest) + SWITCH_CASE(USBRQ_GET_STATUS) /* 0 */ + uchar recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ + if(USB_CFG_IS_SELF_POWERED && recipient == USBRQ_RCPT_DEVICE) + dataPtr[0] = USB_CFG_IS_SELF_POWERED; +#if USB_CFG_IMPLEMENT_HALT + if(recipient == USBRQ_RCPT_ENDPOINT && index == 0x81) /* request status for endpoint 1 */ + dataPtr[0] = usbTxLen1 == USBPID_STALL; +#endif + dataPtr[1] = 0; + len = 2; +#if USB_CFG_IMPLEMENT_HALT + SWITCH_CASE2(USBRQ_CLEAR_FEATURE, USBRQ_SET_FEATURE) /* 1, 3 */ + if(value == 0 && index == 0x81){ /* feature 0 == HALT for endpoint == 1 */ + usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; + usbResetDataToggling(); + } +#endif + SWITCH_CASE(USBRQ_SET_ADDRESS) /* 5 */ + usbNewDeviceAddr = value; + USB_SET_ADDRESS_HOOK(); + SWITCH_CASE(USBRQ_GET_DESCRIPTOR) /* 6 */ + len = usbDriverDescriptor(rq); + goto skipMsgPtrAssignment; + SWITCH_CASE(USBRQ_GET_CONFIGURATION) /* 8 */ + dataPtr = &usbConfiguration; /* send current configuration value */ + len = 1; + SWITCH_CASE(USBRQ_SET_CONFIGURATION) /* 9 */ + usbConfiguration = value; + usbResetStall(); + SWITCH_CASE(USBRQ_GET_INTERFACE) /* 10 */ + len = 1; +#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE + SWITCH_CASE(USBRQ_SET_INTERFACE) /* 11 */ + usbResetDataToggling(); + usbResetStall(); +#endif + SWITCH_DEFAULT /* 7=SET_DESCRIPTOR, 12=SYNC_FRAME */ + /* Should we add an optional hook here? */ + SWITCH_END + usbMsgPtr = (usbMsgPtr_t)dataPtr; +skipMsgPtrAssignment: + return len; +} + +/* ------------------------------------------------------------------------- */ + +/* usbProcessRx() is called for every message received by the interrupt + * routine. It distinguishes between SETUP and DATA packets and processes + * them accordingly. + */ +static inline void usbProcessRx(uchar *data, uchar len) +{ +usbRequest_t *rq = (usbRequest_t *)data; + +/* usbRxToken can be: + * 0x2d 00101101 (USBPID_SETUP for setup data) + * 0xe1 11100001 (USBPID_OUT: data phase of setup transfer) + * 0...0x0f for OUT on endpoint X + */ + DBG2(0x10 + (usbRxToken & 0xf), data, len + 2); /* SETUP=1d, SETUP-DATA=11, OUTx=1x */ + USB_RX_USER_HOOK(data, len) +#if USB_CFG_IMPLEMENT_FN_WRITEOUT + if(usbRxToken < 0x10){ /* OUT to endpoint != 0: endpoint number in usbRxToken */ + usbFunctionWriteOut(data, len); + return; + } +#endif + if(usbRxToken == (uchar)USBPID_SETUP){ + if(len != 8) /* Setup size must be always 8 bytes. Ignore otherwise. */ + return; + usbMsgLen_t replyLen; + usbTxBuf[0] = USBPID_DATA0; /* initialize data toggling */ + usbTxLen = USBPID_NAK; /* abort pending transmit */ + usbMsgFlags = 0; + uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; + if(type != USBRQ_TYPE_STANDARD){ /* standard requests are handled by driver */ + replyLen = usbFunctionSetup(data); + }else{ + replyLen = usbDriverSetup(rq); + } +#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE + if(replyLen == USB_NO_MSG){ /* use user-supplied read/write function */ + /* do some conditioning on replyLen, but on IN transfers only */ + if((rq->bmRequestType & USBRQ_DIR_MASK) != USBRQ_DIR_HOST_TO_DEVICE){ + if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ + replyLen = rq->wLength.bytes[0]; + }else{ + replyLen = rq->wLength.word; + } + } + usbMsgFlags = USB_FLG_USE_USER_RW; + }else /* The 'else' prevents that we limit a replyLen of USB_NO_MSG to the maximum transfer len. */ +#endif + if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ + if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */ + replyLen = rq->wLength.bytes[0]; + }else{ + if(replyLen > rq->wLength.word) /* limit length to max */ + replyLen = rq->wLength.word; + } + usbMsgLen = replyLen; + }else{ /* usbRxToken must be USBPID_OUT, which means data phase of setup (control-out) */ +#if USB_CFG_IMPLEMENT_FN_WRITE + if(usbMsgFlags & USB_FLG_USE_USER_RW){ + uchar rval = usbFunctionWrite(data, len); + if(rval == 0xff){ /* an error occurred */ + usbTxLen = USBPID_STALL; + }else if(rval != 0){ /* This was the final package */ + usbMsgLen = 0; /* answer with a zero-sized data packet */ + } + } +#endif + } +} + +/* ------------------------------------------------------------------------- */ + +/* This function is similar to usbFunctionRead(), but it's also called for + * data handled automatically by the driver (e.g. descriptor reads). + */ +static uchar usbDeviceRead(uchar *data, uchar len) +{ + if(len > 0){ /* don't bother app with 0 sized reads */ +#if USB_CFG_IMPLEMENT_FN_READ + if(usbMsgFlags & USB_FLG_USE_USER_RW){ + len = usbFunctionRead(data, len); + }else +#endif + { + uchar i = len; + usbMsgPtr_t r = usbMsgPtr; + if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ + do{ + uchar c = USB_READ_FLASH(r); /* assign to char size variable to enforce byte ops */ + *data++ = c; + r++; + }while(--i); + }else{ /* RAM data */ + do{ + *data++ = *((uchar *)r); + r++; + }while(--i); + } + usbMsgPtr = r; + } + } + return len; +} + +/* ------------------------------------------------------------------------- */ + +/* usbBuildTxBlock() is called when we have data to transmit and the + * interrupt routine's transmit buffer is empty. + */ +static inline void usbBuildTxBlock(void) +{ +usbMsgLen_t wantLen; +uchar len; + + wantLen = usbMsgLen; + if(wantLen > 8) + wantLen = 8; + usbMsgLen -= wantLen; + usbTxBuf[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* DATA toggling */ + len = usbDeviceRead(usbTxBuf + 1, wantLen); + if(len <= 8){ /* valid data packet */ + usbCrc16Append(&usbTxBuf[1], len); + len += 4; /* length including sync byte */ + if(len < 12) /* a partial package identifies end of message */ + usbMsgLen = USB_NO_MSG; + }else{ + len = USBPID_STALL; /* stall the endpoint */ + usbMsgLen = USB_NO_MSG; + } + usbTxLen = len; + DBG2(0x20, usbTxBuf, len-1); +} + +/* ------------------------------------------------------------------------- */ + +static inline void usbHandleResetHook(uchar notResetState) +{ +#ifdef USB_RESET_HOOK +static uchar wasReset; +uchar isReset = !notResetState; + + if(wasReset != isReset){ + USB_RESET_HOOK(isReset); + wasReset = isReset; + } +#else + notResetState = notResetState; // avoid compiler warning +#endif +} + +/* ------------------------------------------------------------------------- */ + +USB_PUBLIC void usbPoll(void) +{ +schar len; +uchar i; + + len = usbRxLen - 3; + if(len >= 0){ +/* We could check CRC16 here -- but ACK has already been sent anyway. If you + * need data integrity checks with this driver, check the CRC in your app + * code and report errors back to the host. Since the ACK was already sent, + * retries must be handled on application level. + * unsigned crc = usbCrc16(buffer + 1, usbRxLen - 3); + */ + usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len); +#if USB_CFG_HAVE_FLOWCONTROL + if(usbRxLen > 0) /* only mark as available if not inactivated */ + usbRxLen = 0; +#else + usbRxLen = 0; /* mark rx buffer as available */ +#endif + } + if(usbTxLen & 0x10){ /* transmit system idle */ + if(usbMsgLen != USB_NO_MSG){ /* transmit data pending? */ + usbBuildTxBlock(); + } + } + for(i = 20; i > 0; i--){ + uchar usbLineStatus = USBIN & USBMASK; + if(usbLineStatus != 0) /* SE0 has ended */ + goto isNotReset; + } + /* RESET condition, called multiple times during reset */ + usbNewDeviceAddr = 0; + usbDeviceAddr = 0; + usbResetStall(); + DBG1(0xff, 0, 0); +isNotReset: + usbHandleResetHook(i); +} + +/* ------------------------------------------------------------------------- */ + +USB_PUBLIC void usbInit(void) +{ +#if USB_INTR_CFG_SET != 0 + USB_INTR_CFG |= USB_INTR_CFG_SET; +#endif +#if USB_INTR_CFG_CLR != 0 + USB_INTR_CFG &= ~(USB_INTR_CFG_CLR); +#endif + USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT); + usbResetDataToggling(); +#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE + usbTxLen1 = USBPID_NAK; +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 + usbTxLen3 = USBPID_NAK; +#endif +#endif +} + +/* ------------------------------------------------------------------------- */ diff --git a/arduino code/libraries/HIDSerial/usbdrv.h b/arduino code/libraries/HIDSerial/usbdrv.h new file mode 100644 index 0000000..0d71bf5 --- /dev/null +++ b/arduino code/libraries/HIDSerial/usbdrv.h @@ -0,0 +1,782 @@ +/* Name: usbdrv.h + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +#ifndef __usbdrv_h_included__ +#define __usbdrv_h_included__ +#include "usbconfig.h" +#include "usbportability.h" + +/* +Hardware Prerequisites: +======================= +USB lines D+ and D- MUST be wired to the same I/O port. We recommend that D+ +triggers the interrupt (best achieved by using INT0 for D+), but it is also +possible to trigger the interrupt from D-. If D- is used, interrupts are also +triggered by SOF packets. D- requires a pull-up of 1.5k to +3.5V (and the +device must be powered at 3.5V) to identify as low-speed USB device. A +pull-down or pull-up of 1M SHOULD be connected from D+ to +3.5V to prevent +interference when no USB master is connected. If you use Zener diodes to limit +the voltage on D+ and D-, you MUST use a pull-down resistor, not a pull-up. +We use D+ as interrupt source and not D- because it does not trigger on +keep-alive and RESET states. If you want to count keep-alive events with +USB_COUNT_SOF, you MUST use D- as an interrupt source. + +As a compile time option, the 1.5k pull-up resistor on D- can be made +switchable to allow the device to disconnect at will. See the definition of +usbDeviceConnect() and usbDeviceDisconnect() further down in this file. + +Please adapt the values in usbconfig.h according to your hardware! + +The device MUST be clocked at exactly 12 MHz, 15 MHz, 16 MHz or 20 MHz +or at 12.8 MHz resp. 16.5 MHz +/- 1%. See usbconfig-prototype.h for details. + + +Limitations: +============ +Robustness with respect to communication errors: +The driver assumes error-free communication. It DOES check for errors in +the PID, but does NOT check bit stuffing errors, SE0 in middle of a byte, +token CRC (5 bit) and data CRC (16 bit). CRC checks can not be performed due +to timing constraints: We must start sending a reply within 7 bit times. +Bit stuffing and misplaced SE0 would have to be checked in real-time, but CPU +performance does not permit that. The driver does not check Data0/Data1 +toggling, but application software can implement the check. + +Input characteristics: +Since no differential receiver circuit is used, electrical interference +robustness may suffer. The driver samples only one of the data lines with +an ordinary I/O pin's input characteristics. However, since this is only a +low speed USB implementation and the specification allows for 8 times the +bit rate over the same hardware, we should be on the safe side. Even the spec +requires detection of asymmetric states at high bit rate for SE0 detection. + +Number of endpoints: +The driver supports the following endpoints: + +- Endpoint 0, the default control endpoint. +- Any number of interrupt- or bulk-out endpoints. The data is sent to + usbFunctionWriteOut() and USB_CFG_IMPLEMENT_FN_WRITEOUT must be defined + to 1 to activate this feature. The endpoint number can be found in the + global variable 'usbRxToken'. +- One default interrupt- or bulk-in endpoint. This endpoint is used for + interrupt- or bulk-in transfers which are not handled by any other endpoint. + You must define USB_CFG_HAVE_INTRIN_ENDPOINT in order to activate this + feature and call usbSetInterrupt() to send interrupt/bulk data. +- One additional interrupt- or bulk-in endpoint. This was endpoint 3 in + previous versions of this driver but can now be configured to any endpoint + number. You must define USB_CFG_HAVE_INTRIN_ENDPOINT3 in order to activate + this feature and call usbSetInterrupt3() to send interrupt/bulk data. The + endpoint number can be set with USB_CFG_EP3_NUMBER. + +Please note that the USB standard forbids bulk endpoints for low speed devices! +Most operating systems allow them anyway, but the AVR will spend 90% of the CPU +time in the USB interrupt polling for bulk data. + +Maximum data payload: +Data payload of control in and out transfers may be up to 254 bytes. In order +to accept payload data of out transfers, you need to implement +'usbFunctionWrite()'. + +USB Suspend Mode supply current: +The USB standard limits power consumption to 500uA when the bus is in suspend +mode. This is not a problem for self-powered devices since they don't need +bus power anyway. Bus-powered devices can achieve this only by putting the +CPU in sleep mode. The driver does not implement suspend handling by itself. +However, the application may implement activity monitoring and wakeup from +sleep. The host sends regular SE0 states on the bus to keep it active. These +SE0 states can be detected by using D- as the interrupt source. Define +USB_COUNT_SOF to 1 and use the global variable usbSofCount to check for bus +activity. + +Operation without an USB master: +The driver behaves neutral without connection to an USB master if D- reads +as 1. To avoid spurious interrupts, we recommend a high impedance (e.g. 1M) +pull-down or pull-up resistor on D+ (interrupt). If Zener diodes are used, +use a pull-down. If D- becomes statically 0, the driver may block in the +interrupt routine. + +Interrupt latency: +The application must ensure that the USB interrupt is not disabled for more +than 25 cycles (this is for 12 MHz, faster clocks allow longer latency). +This implies that all interrupt routines must either have the "ISR_NOBLOCK" +attribute set (see "avr/interrupt.h") or be written in assembler with "sei" +as the first instruction. + +Maximum interrupt duration / CPU cycle consumption: +The driver handles all USB communication during the interrupt service +routine. The routine will not return before an entire USB message is received +and the reply is sent. This may be up to ca. 1200 cycles @ 12 MHz (= 100us) if +the host conforms to the standard. The driver will consume CPU cycles for all +USB messages, even if they address another (low-speed) device on the same bus. + +*/ + +/* ------------------------------------------------------------------------- */ +/* --------------------------- Module Interface ---------------------------- */ +/* ------------------------------------------------------------------------- */ + +#define USBDRV_VERSION 20121206 +/* This define uniquely identifies a driver version. It is a decimal number + * constructed from the driver's release date in the form YYYYMMDD. If the + * driver's behavior or interface changes, you can use this constant to + * distinguish versions. If it is not defined, the driver's release date is + * older than 2006-01-25. + */ + + +#ifndef USB_PUBLIC +#define USB_PUBLIC +#endif +/* USB_PUBLIC is used as declaration attribute for all functions exported by + * the USB driver. The default is no attribute (see above). You may define it + * to static either in usbconfig.h or from the command line if you include + * usbdrv.c instead of linking against it. Including the C module of the driver + * directly in your code saves a couple of bytes in flash memory. + */ + +#ifndef __ASSEMBLER__ +#ifndef uchar +#define uchar unsigned char +#endif +#ifndef schar +#define schar signed char +#endif +/* shortcuts for well defined 8 bit integer types */ + +#if USB_CFG_LONG_TRANSFERS /* if more than 254 bytes transfer size required */ +# define usbMsgLen_t unsigned +#else +# define usbMsgLen_t uchar +#endif +/* usbMsgLen_t is the data type used for transfer lengths. By default, it is + * defined to uchar, allowing a maximum of 254 bytes (255 is reserved for + * USB_NO_MSG below). If the usbconfig.h defines USB_CFG_LONG_TRANSFERS to 1, + * a 16 bit data type is used, allowing up to 16384 bytes (the rest is used + * for flags in the descriptor configuration). + */ +#define USB_NO_MSG ((usbMsgLen_t)-1) /* constant meaning "no message" */ + +#ifndef usbMsgPtr_t +#define usbMsgPtr_t uchar * +#endif +/* Making usbMsgPtr_t a define allows the user of this library to define it to + * an 8 bit type on tiny devices. This reduces code size, especially if the + * compiler supports a tiny memory model. + * The type can be a pointer or scalar type, casts are made where necessary. + * Although it's paradoxical, Gcc 4 generates slightly better code for scalar + * types than for pointers. + */ + +struct usbRequest; /* forward declaration */ + +#ifdef __cplusplus +extern "C"{ +#endif +USB_PUBLIC void usbInit(void); +/* This function must be called before interrupts are enabled and the main + * loop is entered. We exepct that the PORT and DDR bits for D+ and D- have + * not been changed from their default status (which is 0). If you have changed + * them, set both back to 0 (configure them as input with no internal pull-up). + */ +USB_PUBLIC void usbPoll(void); +/* This function must be called at regular intervals from the main loop. + * Maximum delay between calls is somewhat less than 50ms (USB timeout for + * accepting a Setup message). Otherwise the device will not be recognized. + * Please note that debug outputs through the UART take ~ 0.5ms per byte + * at 19200 bps. + */ +#ifdef __cplusplus +} // extern "C" +#endif +extern usbMsgPtr_t usbMsgPtr; +/* This variable may be used to pass transmit data to the driver from the + * implementation of usbFunctionWrite(). It is also used internally by the + * driver for standard control requests. + */ +#ifdef __cplusplus +extern "C"{ +#endif +USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]); +/* This function is called when the driver receives a SETUP transaction from + * the host which is not answered by the driver itself (in practice: class and + * vendor requests). All control transfers start with a SETUP transaction where + * the host communicates the parameters of the following (optional) data + * transfer. The SETUP data is available in the 'data' parameter which can + * (and should) be casted to 'usbRequest_t *' for a more user-friendly access + * to parameters. + * + * If the SETUP indicates a control-in transfer, you should provide the + * requested data to the driver. There are two ways to transfer this data: + * (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data + * block and return the length of the data in 'usbFunctionSetup()'. The driver + * will handle the rest. Or (2) return USB_NO_MSG in 'usbFunctionSetup()'. The + * driver will then call 'usbFunctionRead()' when data is needed. See the + * documentation for usbFunctionRead() for details. + * + * If the SETUP indicates a control-out transfer, the only way to receive the + * data from the host is through the 'usbFunctionWrite()' call. If you + * implement this function, you must return USB_NO_MSG in 'usbFunctionSetup()' + * to indicate that 'usbFunctionWrite()' should be used. See the documentation + * of this function for more information. If you just want to ignore the data + * sent by the host, return 0 in 'usbFunctionSetup()'. + * + * Note that calls to the functions usbFunctionRead() and usbFunctionWrite() + * are only done if enabled by the configuration in usbconfig.h. + */ +USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq); +#ifdef __cplusplus +} // extern "C" +#endif +/* You need to implement this function ONLY if you provide USB descriptors at + * runtime (which is an expert feature). It is very similar to + * usbFunctionSetup() above, but it is called only to request USB descriptor + * data. See the documentation of usbFunctionSetup() above for more info. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT +#ifdef __cplusplus +extern "C"{ +#endif +USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len); +#ifdef __cplusplus +} // extern "C" +#endif +/* This function sets the message which will be sent during the next interrupt + * IN transfer. The message is copied to an internal buffer and must not exceed + * a length of 8 bytes. The message may be 0 bytes long just to indicate the + * interrupt status to the host. + * If you need to transfer more bytes, use a control read after the interrupt. + */ +#define usbInterruptIsReady() (usbTxLen1 & 0x10) +/* This macro indicates whether the last interrupt message has already been + * sent. If you set a new interrupt message before the old was sent, the + * message already buffered will be lost. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len); +#define usbInterruptIsReady3() (usbTxLen3 & 0x10) +/* Same as above for endpoint 3 */ +#endif +#endif /* USB_CFG_HAVE_INTRIN_ENDPOINT */ +#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* simplified interface for backward compatibility */ +#define usbHidReportDescriptor usbDescriptorHidReport +/* should be declared as: PROGMEM char usbHidReportDescriptor[]; */ +/* If you implement an HID device, you need to provide a report descriptor. + * The HID report descriptor syntax is a bit complex. If you understand how + * report descriptors are constructed, we recommend that you use the HID + * Descriptor Tool from usb.org, see http://www.usb.org/developers/hidpage/. + * Otherwise you should probably start with a working example. + */ +#endif /* USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH */ +#if USB_CFG_IMPLEMENT_FN_WRITE +#ifdef __cplusplus +extern "C"{ +#endif +USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len); +#ifdef __cplusplus +} // extern "C" +#endif +/* This function is called by the driver to provide a control transfer's + * payload data (control-out). It is called in chunks of up to 8 bytes. The + * total count provided in the current control transfer can be obtained from + * the 'length' property in the setup data. If an error occurred during + * processing, return 0xff (== -1). The driver will answer the entire transfer + * with a STALL token in this case. If you have received the entire payload + * successfully, return 1. If you expect more data, return 0. If you don't + * know whether the host will send more data (you should know, the total is + * provided in the usbFunctionSetup() call!), return 1. + * NOTE: If you return 0xff for STALL, 'usbFunctionWrite()' may still be called + * for the remaining data. You must continue to return 0xff for STALL in these + * calls. + * In order to get usbFunctionWrite() called, define USB_CFG_IMPLEMENT_FN_WRITE + * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. + */ +#endif /* USB_CFG_IMPLEMENT_FN_WRITE */ +#if USB_CFG_IMPLEMENT_FN_READ +#ifdef __cplusplus +extern "C"{ +#endif +USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len); +#ifdef __cplusplus +} // extern "C" +#endif +/* This function is called by the driver to ask the application for a control + * transfer's payload data (control-in). It is called in chunks of up to 8 + * bytes each. You should copy the data to the location given by 'data' and + * return the actual number of bytes copied. If you return less than requested, + * the control-in transfer is terminated. If you return 0xff, the driver aborts + * the transfer with a STALL token. + * In order to get usbFunctionRead() called, define USB_CFG_IMPLEMENT_FN_READ + * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. + */ +#endif /* USB_CFG_IMPLEMENT_FN_READ */ + +extern uchar usbRxToken; /* may be used in usbFunctionWriteOut() below */ +#if USB_CFG_IMPLEMENT_FN_WRITEOUT +USB_PUBLIC void usbFunctionWriteOut(uchar *data, uchar len); +/* This function is called by the driver when data is received on an interrupt- + * or bulk-out endpoint. The endpoint number can be found in the global + * variable usbRxToken. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT to 1 in + * usbconfig.h to get this function called. + */ +#endif /* USB_CFG_IMPLEMENT_FN_WRITEOUT */ +#ifdef USB_CFG_PULLUP_IOPORTNAME +#define usbDeviceConnect() ((USB_PULLUP_DDR |= (1<device, 1=device->host + * t ..... type: 0=standard, 1=class, 2=vendor, 3=reserved + * r ..... recipient: 0=device, 1=interface, 2=endpoint, 3=other + */ + +/* USB setup recipient values */ +#define USBRQ_RCPT_MASK 0x1f +#define USBRQ_RCPT_DEVICE 0 +#define USBRQ_RCPT_INTERFACE 1 +#define USBRQ_RCPT_ENDPOINT 2 + +/* USB request type values */ +#define USBRQ_TYPE_MASK 0x60 +#define USBRQ_TYPE_STANDARD (0<<5) +#define USBRQ_TYPE_CLASS (1<<5) +#define USBRQ_TYPE_VENDOR (2<<5) + +/* USB direction values: */ +#define USBRQ_DIR_MASK 0x80 +#define USBRQ_DIR_HOST_TO_DEVICE (0<<7) +#define USBRQ_DIR_DEVICE_TO_HOST (1<<7) + +/* USB Standard Requests */ +#define USBRQ_GET_STATUS 0 +#define USBRQ_CLEAR_FEATURE 1 +#define USBRQ_SET_FEATURE 3 +#define USBRQ_SET_ADDRESS 5 +#define USBRQ_GET_DESCRIPTOR 6 +#define USBRQ_SET_DESCRIPTOR 7 +#define USBRQ_GET_CONFIGURATION 8 +#define USBRQ_SET_CONFIGURATION 9 +#define USBRQ_GET_INTERFACE 10 +#define USBRQ_SET_INTERFACE 11 +#define USBRQ_SYNCH_FRAME 12 + +/* USB descriptor constants */ +#define USBDESCR_DEVICE 1 +#define USBDESCR_CONFIG 2 +#define USBDESCR_STRING 3 +#define USBDESCR_INTERFACE 4 +#define USBDESCR_ENDPOINT 5 +#define USBDESCR_HID 0x21 +#define USBDESCR_HID_REPORT 0x22 +#define USBDESCR_HID_PHYS 0x23 + +//#define USBATTR_BUSPOWER 0x80 // USB 1.1 does not define this value any more +#define USBATTR_BUSPOWER 0 +#define USBATTR_SELFPOWER 0x40 +#define USBATTR_REMOTEWAKE 0x20 + +/* USB HID Requests */ +#define USBRQ_HID_GET_REPORT 0x01 +#define USBRQ_HID_GET_IDLE 0x02 +#define USBRQ_HID_GET_PROTOCOL 0x03 +#define USBRQ_HID_SET_REPORT 0x09 +#define USBRQ_HID_SET_IDLE 0x0a +#define USBRQ_HID_SET_PROTOCOL 0x0b + +/* ------------------------------------------------------------------------- */ + +#endif /* __usbdrv_h_included__ */ diff --git a/arduino code/libraries/HIDSerial/usbdrvasm.S b/arduino code/libraries/HIDSerial/usbdrvasm.S new file mode 100644 index 0000000..32ce8ef --- /dev/null +++ b/arduino code/libraries/HIDSerial/usbdrvasm.S @@ -0,0 +1,392 @@ +/* Name: usbdrvasm.S + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2007-06-13 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* +General Description: +This module is the assembler part of the USB driver. This file contains +general code (preprocessor acrobatics and CRC computation) and then includes +the file appropriate for the given clock rate. +*/ + +#define __SFR_OFFSET 0 /* used by avr-libc's register definitions */ +#include "usbportability.h" +#include "usbdrv.h" /* for common defs */ + +/* register names */ +#define x1 r16 +#define x2 r17 +#define shift r18 +#define cnt r19 +#define x3 r20 +#define x4 r21 +#define x5 r22 +#define bitcnt x5 +#define phase x4 +#define leap x4 + +/* Some assembler dependent definitions and declarations: */ + +#ifdef __IAR_SYSTEMS_ASM__ + extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset + extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen + extern usbTxBuf, usbTxStatus1, usbTxStatus3 +# if USB_COUNT_SOF + extern usbSofCount +# endif + public usbCrc16 + public usbCrc16Append + + COMMON INTVEC +# ifndef USB_INTR_VECTOR + ORG INT0_vect +# else /* USB_INTR_VECTOR */ + ORG USB_INTR_VECTOR +# undef USB_INTR_VECTOR +# endif /* USB_INTR_VECTOR */ +# define USB_INTR_VECTOR usbInterruptHandler + rjmp USB_INTR_VECTOR + RSEG CODE + +#else /* __IAR_SYSTEMS_ASM__ */ + +# ifndef USB_INTR_VECTOR /* default to hardware interrupt INT0 */ +# ifdef INT0_vect +# define USB_INTR_VECTOR INT0_vect // this is the "new" define for the vector +# else +# define USB_INTR_VECTOR SIG_INTERRUPT0 // this is the "old" vector +# endif +# endif + .text + .global USB_INTR_VECTOR + .type USB_INTR_VECTOR, @function + .global usbCrc16 + .global usbCrc16Append +#endif /* __IAR_SYSTEMS_ASM__ */ + + +#if USB_INTR_PENDING < 0x40 /* This is an I/O address, use in and out */ +# define USB_LOAD_PENDING(reg) in reg, USB_INTR_PENDING +# define USB_STORE_PENDING(reg) out USB_INTR_PENDING, reg +#else /* It's a memory address, use lds and sts */ +# define USB_LOAD_PENDING(reg) lds reg, USB_INTR_PENDING +# define USB_STORE_PENDING(reg) sts USB_INTR_PENDING, reg +#endif + +#define usbTxLen1 usbTxStatus1 +#define usbTxBuf1 (usbTxStatus1 + 1) +#define usbTxLen3 usbTxStatus3 +#define usbTxBuf3 (usbTxStatus3 + 1) + + +;---------------------------------------------------------------------------- +; Utility functions +;---------------------------------------------------------------------------- + +#ifdef __IAR_SYSTEMS_ASM__ +/* Register assignments for usbCrc16 on IAR cc */ +/* Calling conventions on IAR: + * First parameter passed in r16/r17, second in r18/r19 and so on. + * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) + * Result is passed in r16/r17 + * In case of the "tiny" memory model, pointers are only 8 bit with no + * padding. We therefore pass argument 1 as "16 bit unsigned". + */ +RTMODEL "__rt_version", "3" +/* The line above will generate an error if cc calling conventions change. + * The value "3" above is valid for IAR 4.10B/W32 + */ +# define argLen r18 /* argument 2 */ +# define argPtrL r16 /* argument 1 */ +# define argPtrH r17 /* argument 1 */ + +# define resCrcL r16 /* result */ +# define resCrcH r17 /* result */ + +# define ptrL ZL +# define ptrH ZH +# define ptr Z +# define byte r22 +# define bitCnt r19 +# define polyL r20 +# define polyH r21 +# define scratch r23 + +#else /* __IAR_SYSTEMS_ASM__ */ +/* Register assignments for usbCrc16 on gcc */ +/* Calling conventions on gcc: + * First parameter passed in r24/r25, second in r22/23 and so on. + * Callee must preserve r1-r17, r28/r29 + * Result is passed in r24/r25 + */ +# define argLen r22 /* argument 2 */ +# define argPtrL r24 /* argument 1 */ +# define argPtrH r25 /* argument 1 */ + +# define resCrcL r24 /* result */ +# define resCrcH r25 /* result */ + +# define ptrL XL +# define ptrH XH +# define ptr x +# define byte r18 +# define bitCnt r19 +# define polyL r20 +# define polyH r21 +# define scratch r23 + +#endif + +#if USB_USE_FAST_CRC + +; This implementation is faster, but has bigger code size +; Thanks to Slawomir Fras (BoskiDialer) for this code! +; It implements the following C pseudo-code: +; unsigned table(unsigned char x) +; { +; unsigned value; +; +; value = (unsigned)x << 6; +; value ^= (unsigned)x << 7; +; if(parity(x)) +; value ^= 0xc001; +; return value; +; } +; unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen) +; { +; unsigned crc = 0xffff; +; +; while(argLen--) +; crc = table(lo8(crc) ^ *argPtr++) ^ hi8(crc); +; return ~crc; +; } + +; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen); +; argPtr r24+25 / r16+r17 +; argLen r22 / r18 +; temp variables: +; byte r18 / r22 +; scratch r23 +; resCrc r24+r25 / r16+r17 +; ptr X / Z +usbCrc16: + mov ptrL, argPtrL + mov ptrH, argPtrH + ldi resCrcL, 0xFF + ldi resCrcH, 0xFF + rjmp usbCrc16LoopTest +usbCrc16ByteLoop: + ld byte, ptr+ + eor resCrcL, byte ; resCrcL is now 'x' in table() + mov byte, resCrcL ; compute parity of 'x' + swap byte + eor byte, resCrcL + mov scratch, byte + lsr byte + lsr byte + eor byte, scratch + inc byte + lsr byte + andi byte, 1 ; byte is now parity(x) + mov scratch, resCrcL + mov resCrcL, resCrcH + eor resCrcL, byte ; low byte of if(parity(x)) value ^= 0xc001; + neg byte + andi byte, 0xc0 + mov resCrcH, byte ; high byte of if(parity(x)) value ^= 0xc001; + clr byte + lsr scratch + ror byte + eor resCrcH, scratch + eor resCrcL, byte + lsr scratch + ror byte + eor resCrcH, scratch + eor resCrcL, byte +usbCrc16LoopTest: + subi argLen, 1 + brsh usbCrc16ByteLoop + com resCrcL + com resCrcH + ret + +#else /* USB_USE_FAST_CRC */ + +; This implementation is slower, but has less code size +; +; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen); +; argPtr r24+25 / r16+r17 +; argLen r22 / r18 +; temp variables: +; byte r18 / r22 +; bitCnt r19 +; poly r20+r21 +; scratch r23 +; resCrc r24+r25 / r16+r17 +; ptr X / Z +usbCrc16: + mov ptrL, argPtrL + mov ptrH, argPtrH + ldi resCrcL, 0 + ldi resCrcH, 0 + ldi polyL, lo8(0xa001) + ldi polyH, hi8(0xa001) + com argLen ; argLen = -argLen - 1: modified loop to ensure that carry is set + ldi bitCnt, 0 ; loop counter with starnd condition = end condition + rjmp usbCrcLoopEntry +usbCrcByteLoop: + ld byte, ptr+ + eor resCrcL, byte +usbCrcBitLoop: + ror resCrcH ; carry is always set here (see brcs jumps to here) + ror resCrcL + brcs usbCrcNoXor + eor resCrcL, polyL + eor resCrcH, polyH +usbCrcNoXor: + subi bitCnt, 224 ; (8 * 224) % 256 = 0; this loop iterates 8 times + brcs usbCrcBitLoop +usbCrcLoopEntry: + subi argLen, -1 + brcs usbCrcByteLoop +usbCrcReady: + ret +; Thanks to Reimar Doeffinger for optimizing this CRC routine! + +#endif /* USB_USE_FAST_CRC */ + +; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len); +usbCrc16Append: + rcall usbCrc16 + st ptr+, resCrcL + st ptr+, resCrcH + ret + +#undef argLen +#undef argPtrL +#undef argPtrH +#undef resCrcL +#undef resCrcH +#undef ptrL +#undef ptrH +#undef ptr +#undef byte +#undef bitCnt +#undef polyL +#undef polyH +#undef scratch + + +#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH +#ifdef __IAR_SYSTEMS_ASM__ +/* Register assignments for usbMeasureFrameLength on IAR cc */ +/* Calling conventions on IAR: + * First parameter passed in r16/r17, second in r18/r19 and so on. + * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) + * Result is passed in r16/r17 + * In case of the "tiny" memory model, pointers are only 8 bit with no + * padding. We therefore pass argument 1 as "16 bit unsigned". + */ +# define resL r16 +# define resH r17 +# define cnt16L r30 +# define cnt16H r31 +# define cntH r18 + +#else /* __IAR_SYSTEMS_ASM__ */ +/* Register assignments for usbMeasureFrameLength on gcc */ +/* Calling conventions on gcc: + * First parameter passed in r24/r25, second in r22/23 and so on. + * Callee must preserve r1-r17, r28/r29 + * Result is passed in r24/r25 + */ +# define resL r24 +# define resH r25 +# define cnt16L r24 +# define cnt16H r25 +# define cntH r26 +#endif +# define cnt16 cnt16L + +; extern unsigned usbMeasurePacketLength(void); +; returns time between two idle strobes in multiples of 7 CPU clocks +.global usbMeasureFrameLength +usbMeasureFrameLength: + ldi cntH, 6 ; wait ~ 10 ms for D- == 0 + clr cnt16L + clr cnt16H +usbMFTime16: + dec cntH + breq usbMFTimeout +usbMFWaitStrobe: ; first wait for D- == 0 (idle strobe) + sbiw cnt16, 1 ;[0] [6] + breq usbMFTime16 ;[2] + sbic USBIN, USBMINUS ;[3] + rjmp usbMFWaitStrobe ;[4] +usbMFWaitIdle: ; then wait until idle again + sbis USBIN, USBMINUS ;1 wait for D- == 1 + rjmp usbMFWaitIdle ;2 + ldi cnt16L, 1 ;1 represents cycles so far + clr cnt16H ;1 +usbMFWaitLoop: + in cntH, USBIN ;[0] [7] + adiw cnt16, 1 ;[1] + breq usbMFTimeout ;[3] + andi cntH, USBMASK ;[4] + brne usbMFWaitLoop ;[5] +usbMFTimeout: +#if resL != cnt16L + mov resL, cnt16L + mov resH, cnt16H +#endif + ret + +#undef resL +#undef resH +#undef cnt16 +#undef cnt16L +#undef cnt16H +#undef cntH + +#endif /* USB_CFG_HAVE_MEASURE_FRAME_LENGTH */ + +;---------------------------------------------------------------------------- +; Now include the clock rate specific code +;---------------------------------------------------------------------------- + +#ifndef USB_CFG_CLOCK_KHZ +# ifdef F_CPU +# define USB_CFG_CLOCK_KHZ (F_CPU/1000) +# else +# error "USB_CFG_CLOCK_KHZ not defined in usbconfig.h and no F_CPU set!" +# endif +#endif + +#if USB_CFG_CHECK_CRC /* separate dispatcher for CRC type modules */ +# if USB_CFG_CLOCK_KHZ == 18000 +# include "usbdrvasm18-crc.inc" +# else +# error "USB_CFG_CLOCK_KHZ is not one of the supported crc-rates!" +# endif +#else /* USB_CFG_CHECK_CRC */ +# if USB_CFG_CLOCK_KHZ == 12000 +# include "usbdrvasm12.inc" +# elif USB_CFG_CLOCK_KHZ == 12800 +# include "usbdrvasm128.inc" +# elif USB_CFG_CLOCK_KHZ == 15000 +# include "usbdrvasm15.inc" +# elif USB_CFG_CLOCK_KHZ == 16000 +# include "usbdrvasm16.inc" +# elif USB_CFG_CLOCK_KHZ == 16500 +# include "usbdrvasm165.inc" +# elif USB_CFG_CLOCK_KHZ == 20000 +# include "usbdrvasm20.inc" +# else +# error "USB_CFG_CLOCK_KHZ is not one of the supported non-crc-rates!" +# endif +#endif /* USB_CFG_CHECK_CRC */ diff --git a/arduino code/libraries/HIDSerial/usbdrvasm.asm b/arduino code/libraries/HIDSerial/usbdrvasm.asm new file mode 100644 index 0000000..fb66934 --- /dev/null +++ b/arduino code/libraries/HIDSerial/usbdrvasm.asm @@ -0,0 +1,20 @@ +/* Name: usbdrvasm.asm + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2006-03-01 + * Tabsize: 4 + * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* +General Description: +The IAR compiler/assembler system prefers assembler files with file extension +".asm". We simply provide this file as an alias for usbdrvasm.S. + +Thanks to Oleg Semyonov for his help with the IAR tools port! +*/ + +#include "usbdrvasm.S" + +end diff --git a/arduino code/libraries/HIDSerial/usbdrvasm12.inc b/arduino code/libraries/HIDSerial/usbdrvasm12.inc new file mode 100644 index 0000000..d3bd056 --- /dev/null +++ b/arduino code/libraries/HIDSerial/usbdrvasm12.inc @@ -0,0 +1,392 @@ +/* Name: usbdrvasm12.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 12 MHz version of the asssembler part of the USB driver. It +requires a 12 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! + + +Timing constraints according to spec (in bit times): +timing subject min max CPUcycles +--------------------------------------------------------------------------- +EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2 16 16-128 +EOP of IN to sync pattern of DATA0 (rx, then tx) 2 7.5 16-60 +DATAx (rx) to ACK/NAK/STALL (tx) 2 7.5 16-60 +*/ + +;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! +;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled +;max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable +;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes +;Numbers in brackets are maximum cycles since SOF. +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt + push YL ;2 [35] push only what is necessary to sync with edge ASAP + in YL, SREG ;1 [37] + push YL ;2 [39] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of 1/4 bit which meets the spec. + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push YH ;2 [2] + lds YL, usbInputBufOffset;2 [4] + clr YH ;1 [5] + subi YL, lo8(-(usbRxBuf));1 [6] + sbci YH, hi8(-(usbRxBuf));1 [7] + + sbis USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early] + rjmp haveTwoBitsK ;2 [10] + pop YH ;2 [11] undo the push from before + rjmp waitForK ;2 [13] this was not the end of sync, retry +haveTwoBitsK: +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- + push shift ;2 [16] + push x1 ;2 [12] + push x2 ;2 [14] + + in x1, USBIN ;1 [17] <-- sample bit 0 + ldi shift, 0xff ;1 [18] + bst x1, USBMINUS ;1 [19] + bld shift, 0 ;1 [20] + push x3 ;2 [22] + push cnt ;2 [24] + + in x2, USBIN ;1 [25] <-- sample bit 1 + ser x3 ;1 [26] [inserted init instruction] + eor x1, x2 ;1 [27] + bst x1, USBMINUS ;1 [28] + bld shift, 1 ;1 [29] + ldi cnt, USB_BUFSIZE;1 [30] [inserted init instruction] + rjmp rxbit2 ;2 [32] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +unstuff0: ;1 (branch taken) + andi x3, ~0x01 ;1 [15] + mov x1, x2 ;1 [16] x2 contains last sampled (stuffed) bit + in x2, USBIN ;1 [17] <-- sample bit 1 again + ori shift, 0x01 ;1 [18] + rjmp didUnstuff0 ;2 [20] + +unstuff1: ;1 (branch taken) + mov x2, x1 ;1 [21] x1 contains last sampled (stuffed) bit + andi x3, ~0x02 ;1 [22] + ori shift, 0x02 ;1 [23] + nop ;1 [24] + in x1, USBIN ;1 [25] <-- sample bit 2 again + rjmp didUnstuff1 ;2 [27] + +unstuff2: ;1 (branch taken) + andi x3, ~0x04 ;1 [29] + ori shift, 0x04 ;1 [30] + mov x1, x2 ;1 [31] x2 contains last sampled (stuffed) bit + nop ;1 [32] + in x2, USBIN ;1 [33] <-- sample bit 3 + rjmp didUnstuff2 ;2 [35] + +unstuff3: ;1 (branch taken) + in x2, USBIN ;1 [34] <-- sample stuffed bit 3 [one cycle too late] + andi x3, ~0x08 ;1 [35] + ori shift, 0x08 ;1 [36] + rjmp didUnstuff3 ;2 [38] + +unstuff4: ;1 (branch taken) + andi x3, ~0x10 ;1 [40] + in x1, USBIN ;1 [41] <-- sample stuffed bit 4 + ori shift, 0x10 ;1 [42] + rjmp didUnstuff4 ;2 [44] + +unstuff5: ;1 (branch taken) + andi x3, ~0x20 ;1 [48] + in x2, USBIN ;1 [49] <-- sample stuffed bit 5 + ori shift, 0x20 ;1 [50] + rjmp didUnstuff5 ;2 [52] + +unstuff6: ;1 (branch taken) + andi x3, ~0x40 ;1 [56] + in x1, USBIN ;1 [57] <-- sample stuffed bit 6 + ori shift, 0x40 ;1 [58] + rjmp didUnstuff6 ;2 [60] + +; extra jobs done during bit interval: +; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs] +; bit 1: se0 check +; bit 2: overflow check +; bit 3: recovery from delay [bit 0 tasks took too long] +; bit 4: none +; bit 5: none +; bit 6: none +; bit 7: jump, eor +rxLoop: + eor x3, shift ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others + in x1, USBIN ;1 [1] <-- sample bit 0 + st y+, x3 ;2 [3] store data + ser x3 ;1 [4] + nop ;1 [5] + eor x2, x1 ;1 [6] + bst x2, USBMINUS;1 [7] + bld shift, 0 ;1 [8] + in x2, USBIN ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed) + andi x2, USBMASK ;1 [10] + breq se0 ;1 [11] SE0 check for bit 1 + andi shift, 0xf9 ;1 [12] +didUnstuff0: + breq unstuff0 ;1 [13] + eor x1, x2 ;1 [14] + bst x1, USBMINUS;1 [15] + bld shift, 1 ;1 [16] +rxbit2: + in x1, USBIN ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed) + andi shift, 0xf3 ;1 [18] + breq unstuff1 ;1 [19] do remaining work for bit 1 +didUnstuff1: + subi cnt, 1 ;1 [20] + brcs overflow ;1 [21] loop control + eor x2, x1 ;1 [22] + bst x2, USBMINUS;1 [23] + bld shift, 2 ;1 [24] + in x2, USBIN ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed) + andi shift, 0xe7 ;1 [26] + breq unstuff2 ;1 [27] +didUnstuff2: + eor x1, x2 ;1 [28] + bst x1, USBMINUS;1 [29] + bld shift, 3 ;1 [30] +didUnstuff3: + andi shift, 0xcf ;1 [31] + breq unstuff3 ;1 [32] + in x1, USBIN ;1 [33] <-- sample bit 4 + eor x2, x1 ;1 [34] + bst x2, USBMINUS;1 [35] + bld shift, 4 ;1 [36] +didUnstuff4: + andi shift, 0x9f ;1 [37] + breq unstuff4 ;1 [38] + nop2 ;2 [40] + in x2, USBIN ;1 [41] <-- sample bit 5 + eor x1, x2 ;1 [42] + bst x1, USBMINUS;1 [43] + bld shift, 5 ;1 [44] +didUnstuff5: + andi shift, 0x3f ;1 [45] + breq unstuff5 ;1 [46] + nop2 ;2 [48] + in x1, USBIN ;1 [49] <-- sample bit 6 + eor x2, x1 ;1 [50] + bst x2, USBMINUS;1 [51] + bld shift, 6 ;1 [52] +didUnstuff6: + cpi shift, 0x02 ;1 [53] + brlo unstuff6 ;1 [54] + nop2 ;2 [56] + in x2, USBIN ;1 [57] <-- sample bit 7 + eor x1, x2 ;1 [58] + bst x1, USBMINUS;1 [59] + bld shift, 7 ;1 [60] +didUnstuff7: + cpi shift, 0x04 ;1 [61] + brsh rxLoop ;2 [63] loop control +unstuff7: + andi x3, ~0x80 ;1 [63] + ori shift, 0x80 ;1 [64] + in x2, USBIN ;1 [65] <-- sample stuffed bit 7 + nop ;1 [66] + rjmp didUnstuff7 ;2 [68] + +macro POP_STANDARD ; 12 cycles + pop cnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;---------------------------------------------------------------------------- +; Transmitting data +;---------------------------------------------------------------------------- + +txByteLoop: +txBitloop: +stuffN1Delay: ; [03] + ror shift ;[-5] [11] [59] + brcc doExorN1 ;[-4] [60] + subi x4, 1 ;[-3] + brne commonN1 ;[-2] + lsl shift ;[-1] compensate ror after rjmp stuffDelay + nop ;[00] stuffing consists of just waiting 8 cycles + rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear + +sendNakAndReti: ;0 [-19] 19 cycles until SOP + ldi x3, USBPID_NAK ;1 [-18] + rjmp usbSendX3 ;2 [-16] +sendAckAndReti: ;0 [-19] 19 cycles until SOP + ldi x3, USBPID_ACK ;1 [-18] + rjmp usbSendX3 ;2 [-16] +sendCntAndReti: ;0 [-17] 17 cycles until SOP + mov x3, cnt ;1 [-16] +usbSendX3: ;0 [-16] + ldi YL, 20 ;1 [-15] 'x3' is R20 + ldi YH, 0 ;1 [-14] + ldi cnt, 2 ;1 [-13] +; rjmp usbSendAndReti fallthrough + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 +; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 +; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte +;uses: x1...x2, x4, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x4 = bitstuff cnt] +;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) +usbSendAndReti: + in x2, USBDDR ;[-12] 12 cycles until SOP + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS ;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + out USBDDR, x2 ;[-8] <--- acquire bus + in x1, USBOUT ;[-7] port mirror for tx loop + ldi shift, 0x40 ;[-6] sync byte is first byte sent (we enter loop after ror) + ldi x2, USBMASK ;[-5] + push x4 ;[-4] +doExorN1: + eor x1, x2 ;[-2] [06] [62] + ldi x4, 6 ;[-1] [07] [63] +commonN1: +stuffN2Delay: + out USBOUT, x1 ;[00] [08] [64] <--- set bit + ror shift ;[01] + brcc doExorN2 ;[02] + subi x4, 1 ;[03] + brne commonN2 ;[04] + lsl shift ;[05] compensate ror after rjmp stuffDelay + rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear +doExorN2: + eor x1, x2 ;[04] [12] + ldi x4, 6 ;[05] [13] +commonN2: + nop ;[06] [14] + subi cnt, 171 ;[07] [15] trick: (3 * 171) & 0xff = 1 + out USBOUT, x1 ;[08] [16] <--- set bit + brcs txBitloop ;[09] [25] [41] + +stuff6Delay: + ror shift ;[42] [50] + brcc doExor6 ;[43] + subi x4, 1 ;[44] + brne common6 ;[45] + lsl shift ;[46] compensate ror after rjmp stuffDelay + nop ;[47] stuffing consists of just waiting 8 cycles + rjmp stuff6Delay ;[48] after ror, C bit is reliably clear +doExor6: + eor x1, x2 ;[45] [53] + ldi x4, 6 ;[46] +common6: +stuff7Delay: + ror shift ;[47] [55] + out USBOUT, x1 ;[48] <--- set bit + brcc doExor7 ;[49] + subi x4, 1 ;[50] + brne common7 ;[51] + lsl shift ;[52] compensate ror after rjmp stuffDelay + rjmp stuff7Delay ;[53] after ror, C bit is reliably clear +doExor7: + eor x1, x2 ;[51] [59] + ldi x4, 6 ;[52] +common7: + ld shift, y+ ;[53] + tst cnt ;[55] + out USBOUT, x1 ;[56] <--- set bit + brne txByteLoop ;[57] + +;make SE0: + cbr x1, USBMASK ;[58] prepare SE0 [spec says EOP may be 15 to 18 cycles] + lds x2, usbNewDeviceAddr;[59] + lsl x2 ;[61] we compare with left shifted address + subi YL, 2 + 20 ;[62] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[63] + out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[01] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 12.5625 MHz +max frequency: 69.286 cycles for 8 bit -> 12.99 MHz +nominal frequency: 12.77 MHz ( = sqrt(min * max)) + +sampling positions: (next even number in range [+/- 0.5]) +cycle index range: 0 ... 66 +bits: +.5, 8.875, 17.25, 25.625, 34, 42.375, 50.75, 59.125 +[0/1], [9], [17], [25/+26], [34], [+42/43], [51], [59] + +bit number: 0 1 2 3 4 5 6 7 +spare cycles 1 2 1 2 1 1 1 0 + +operations to perform: duration cycle + ---------------- + eor fix, shift 1 -> 00 + andi phase, USBMASK 1 -> 08 + breq se0 1 -> 16 (moved to 11) + st y+, data 2 -> 24, 25 + mov data, fix 1 -> 33 + ser data 1 -> 41 + subi cnt, 1 1 -> 49 + brcs overflow 1 -> 50 + +layout of samples and operations: +[##] = sample bit +<##> = sample phase +*##* = operation + +0: *00* [01] 02 03 04 <05> 06 07 +1: *08* [09] 10 11 12 <13> 14 15 *16* +2: [17] 18 19 20 <21> 22 23 +3: *24* *25* [26] 27 28 29 <30> 31 32 +4: *33* [34] 35 36 37 <38> 39 40 +5: *41* [42] 43 44 45 <46> 47 48 +6: *49* *50* [51] 52 53 54 <55> 56 57 58 +7: [59] 60 61 62 <63> 64 65 66 +*****************************************************************************/ + +/* we prefer positive expressions (do if condition) instead of negative + * (skip if condition), therefore use defines for skip instructions: + */ +#define ifioclr sbis +#define ifioset sbic +#define ifrclr sbrs +#define ifrset sbrc + +/* The registers "fix" and "data" swap their meaning during the loop. Use + * defines to keep their name constant. + */ +#define fix x2 +#define data x1 +#undef phase /* phase has a default definition to x4 */ +#define phase x3 + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt, r0 + push YL ;2 push only what is necessary to sync with edge ASAP + in YL, SREG ;1 + push YL ;2 +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of 1/4 bit which meets the spec. + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS ;[0] + rjmp foundK ;[1] +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError + +foundK: +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push YH ;[2] + lds YL, usbInputBufOffset;[4] + clr YH ;[6] + subi YL, lo8(-(usbRxBuf));[7] + sbci YH, hi8(-(usbRxBuf));[8] + + sbis USBIN, USBMINUS ;[9] we want two bits K [we want to sample at 8 + 4 - 1.5 = 10.5] + rjmp haveTwoBitsK ;[10] + pop YH ;[11] undo the push from before + rjmp waitForK ;[13] this was not the end of sync, retry +haveTwoBitsK: +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +#define fix x2 +#define data x1 + + push shift ;[12] + push x1 ;[14] + push x2 ;[16] + ldi shift, 0x80 ;[18] prevent bit-unstuffing but init low bits to 0 + ifioset USBIN, USBMINUS ;[19] [01] <--- bit 0 [10.5 + 8 = 18.5] + ori shift, 1<<0 ;[02] + push x3 ;[03] + push cnt ;[05] + push r0 ;[07] + ifioset USBIN, USBMINUS ;[09] <--- bit 1 + ori shift, 1<<1 ;[10] + ser fix ;[11] + ldi cnt, USB_BUFSIZE ;[12] + mov data, shift ;[13] + lsl shift ;[14] + nop2 ;[15] + ifioset USBIN, USBMINUS ;[17] <--- bit 2 + ori data, 3<<2 ;[18] store in bit 2 AND bit 3 + eor shift, data ;[19] do nrzi decoding + andi data, 1<<3 ;[20] + in phase, USBIN ;[21] <- phase + brne jumpToEntryAfterSet ;[22] if USBMINS at bit 3 was 1 + nop ;[23] + rjmp entryAfterClr ;[24] +jumpToEntryAfterSet: + rjmp entryAfterSet ;[24] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +#undef fix +#define fix x1 +#undef data +#define data x2 + +bit7IsSet: + ifrclr phase, USBMINUS ;[62] check phase only if D- changed + lpm ;[63] + in phase, USBIN ;[64] <- phase (one cycle too late) + ori shift, 1 << 7 ;[65] + nop ;[66] +;;;;rjmp bit0AfterSet ; -> [00] == [67] moved block up to save jump +bit0AfterSet: + eor fix, shift ;[00] +#undef fix +#define fix x2 +#undef data +#define data x1 /* we now have result in data, fix is reset to 0xff */ + ifioclr USBIN, USBMINUS ;[01] <--- sample 0 + rjmp bit0IsClr ;[02] + andi shift, ~(7 << 0) ;[03] + breq unstuff0s ;[04] + in phase, USBIN ;[05] <- phase + rjmp bit1AfterSet ;[06] +unstuff0s: + in phase, USBIN ;[06] <- phase (one cycle too late) + andi fix, ~(1 << 0) ;[07] + ifioclr USBIN, USBMINUS ;[00] + ifioset USBIN, USBPLUS ;[01] + rjmp bit0IsClr ;[02] executed if first expr false or second true +se0AndStore: ; executed only if both bits 0 + st y+, x1 ;[15/17] cycles after start of byte + rjmp se0 ;[17/19] + +bit0IsClr: + ifrset phase, USBMINUS ;[04] check phase only if D- changed + lpm ;[05] + in phase, USBIN ;[06] <- phase (one cycle too late) + ori shift, 1 << 0 ;[07] +bit1AfterClr: + andi phase, USBMASK ;[08] + ifioset USBIN, USBMINUS ;[09] <--- sample 1 + rjmp bit1IsSet ;[10] + breq se0AndStore ;[11] if D- was 0 in bits 0 AND 1 and D+ was 0 in between, we have SE0 + andi shift, ~(7 << 1) ;[12] + in phase, USBIN ;[13] <- phase + breq unstuff1c ;[14] + rjmp bit2AfterClr ;[15] +unstuff1c: + andi fix, ~(1 << 1) ;[16] + nop2 ;[08] + nop2 ;[10] +bit1IsSet: + ifrclr phase, USBMINUS ;[12] check phase only if D- changed + lpm ;[13] + in phase, USBIN ;[14] <- phase (one cycle too late) + ori shift, 1 << 1 ;[15] + nop ;[16] +bit2AfterSet: + ifioclr USBIN, USBMINUS ;[17] <--- sample 2 + rjmp bit2IsClr ;[18] + andi shift, ~(7 << 2) ;[19] + breq unstuff2s ;[20] + in phase, USBIN ;[21] <- phase + rjmp bit3AfterSet ;[22] +unstuff2s: + in phase, USBIN ;[22] <- phase (one cycle too late) + andi fix, ~(1 << 2) ;[23] + nop2 ;[16] + nop2 ;[18] +bit2IsClr: + ifrset phase, USBMINUS ;[20] check phase only if D- changed + lpm ;[21] + in phase, USBIN ;[22] <- phase (one cycle too late) + ori shift, 1 << 2 ;[23] +bit3AfterClr: + st y+, data ;[24] +entryAfterClr: + ifioset USBIN, USBMINUS ;[26] <--- sample 3 + rjmp bit3IsSet ;[27] + andi shift, ~(7 << 3) ;[28] + breq unstuff3c ;[29] + in phase, USBIN ;[30] <- phase + rjmp bit4AfterClr ;[31] +unstuff3c: + in phase, USBIN ;[31] <- phase (one cycle too late) + andi fix, ~(1 << 3) ;[32] + nop2 ;[25] + nop2 ;[27] +bit3IsSet: + ifrclr phase, USBMINUS ;[29] check phase only if D- changed + lpm ;[30] + in phase, USBIN ;[31] <- phase (one cycle too late) + ori shift, 1 << 3 ;[32] +bit4AfterSet: + mov data, fix ;[33] undo this move by swapping defines +#undef fix +#define fix x1 +#undef data +#define data x2 + ifioclr USBIN, USBMINUS ;[34] <--- sample 4 + rjmp bit4IsClr ;[35] + andi shift, ~(7 << 4) ;[36] + breq unstuff4s ;[37] + in phase, USBIN ;[38] <- phase + rjmp bit5AfterSet ;[39] +unstuff4s: + in phase, USBIN ;[39] <- phase (one cycle too late) + andi fix, ~(1 << 4) ;[40] + nop2 ;[33] + nop2 ;[35] +bit4IsClr: + ifrset phase, USBMINUS ;[37] check phase only if D- changed + lpm ;[38] + in phase, USBIN ;[39] <- phase (one cycle too late) + ori shift, 1 << 4 ;[40] +bit5AfterClr: + ser data ;[41] + ifioset USBIN, USBMINUS ;[42] <--- sample 5 + rjmp bit5IsSet ;[43] + andi shift, ~(7 << 5) ;[44] + breq unstuff5c ;[45] + in phase, USBIN ;[46] <- phase + rjmp bit6AfterClr ;[47] +unstuff5c: + in phase, USBIN ;[47] <- phase (one cycle too late) + andi fix, ~(1 << 5) ;[48] + nop2 ;[41] + nop2 ;[43] +bit5IsSet: + ifrclr phase, USBMINUS ;[45] check phase only if D- changed + lpm ;[46] + in phase, USBIN ;[47] <- phase (one cycle too late) + ori shift, 1 << 5 ;[48] +bit6AfterSet: + subi cnt, 1 ;[49] + brcs jumpToOverflow ;[50] + ifioclr USBIN, USBMINUS ;[51] <--- sample 6 + rjmp bit6IsClr ;[52] + andi shift, ~(3 << 6) ;[53] + cpi shift, 2 ;[54] + in phase, USBIN ;[55] <- phase + brlt unstuff6s ;[56] + rjmp bit7AfterSet ;[57] + +jumpToOverflow: + rjmp overflow + +unstuff6s: + andi fix, ~(1 << 6) ;[50] + lpm ;[51] +bit6IsClr: + ifrset phase, USBMINUS ;[54] check phase only if D- changed + lpm ;[55] + in phase, USBIN ;[56] <- phase (one cycle too late) + ori shift, 1 << 6 ;[57] + nop ;[58] +bit7AfterClr: + ifioset USBIN, USBMINUS ;[59] <--- sample 7 + rjmp bit7IsSet ;[60] + andi shift, ~(1 << 7) ;[61] + cpi shift, 4 ;[62] + in phase, USBIN ;[63] <- phase + brlt unstuff7c ;[64] + rjmp bit0AfterClr ;[65] -> [00] == [67] +unstuff7c: + andi fix, ~(1 << 7) ;[58] + nop ;[59] + rjmp bit7IsSet ;[60] + +bit7IsClr: + ifrset phase, USBMINUS ;[62] check phase only if D- changed + lpm ;[63] + in phase, USBIN ;[64] <- phase (one cycle too late) + ori shift, 1 << 7 ;[65] + nop ;[66] +;;;;rjmp bit0AfterClr ; -> [00] == [67] moved block up to save jump +bit0AfterClr: + eor fix, shift ;[00] +#undef fix +#define fix x2 +#undef data +#define data x1 /* we now have result in data, fix is reset to 0xff */ + ifioset USBIN, USBMINUS ;[01] <--- sample 0 + rjmp bit0IsSet ;[02] + andi shift, ~(7 << 0) ;[03] + breq unstuff0c ;[04] + in phase, USBIN ;[05] <- phase + rjmp bit1AfterClr ;[06] +unstuff0c: + in phase, USBIN ;[06] <- phase (one cycle too late) + andi fix, ~(1 << 0) ;[07] + ifioclr USBIN, USBMINUS ;[00] + ifioset USBIN, USBPLUS ;[01] + rjmp bit0IsSet ;[02] executed if first expr false or second true + rjmp se0AndStore ;[03] executed only if both bits 0 +bit0IsSet: + ifrclr phase, USBMINUS ;[04] check phase only if D- changed + lpm ;[05] + in phase, USBIN ;[06] <- phase (one cycle too late) + ori shift, 1 << 0 ;[07] +bit1AfterSet: + andi shift, ~(7 << 1) ;[08] compensated by "ori shift, 1<<1" if bit1IsClr + ifioclr USBIN, USBMINUS ;[09] <--- sample 1 + rjmp bit1IsClr ;[10] + breq unstuff1s ;[11] + nop2 ;[12] do not check for SE0 if bit 0 was 1 + in phase, USBIN ;[14] <- phase (one cycle too late) + rjmp bit2AfterSet ;[15] +unstuff1s: + in phase, USBIN ;[13] <- phase + andi fix, ~(1 << 1) ;[14] + lpm ;[07] + nop2 ;[10] +bit1IsClr: + ifrset phase, USBMINUS ;[12] check phase only if D- changed + lpm ;[13] + in phase, USBIN ;[14] <- phase (one cycle too late) + ori shift, 1 << 1 ;[15] + nop ;[16] +bit2AfterClr: + ifioset USBIN, USBMINUS ;[17] <--- sample 2 + rjmp bit2IsSet ;[18] + andi shift, ~(7 << 2) ;[19] + breq unstuff2c ;[20] + in phase, USBIN ;[21] <- phase + rjmp bit3AfterClr ;[22] +unstuff2c: + in phase, USBIN ;[22] <- phase (one cycle too late) + andi fix, ~(1 << 2) ;[23] + nop2 ;[16] + nop2 ;[18] +bit2IsSet: + ifrclr phase, USBMINUS ;[20] check phase only if D- changed + lpm ;[21] + in phase, USBIN ;[22] <- phase (one cycle too late) + ori shift, 1 << 2 ;[23] +bit3AfterSet: + st y+, data ;[24] +entryAfterSet: + ifioclr USBIN, USBMINUS ;[26] <--- sample 3 + rjmp bit3IsClr ;[27] + andi shift, ~(7 << 3) ;[28] + breq unstuff3s ;[29] + in phase, USBIN ;[30] <- phase + rjmp bit4AfterSet ;[31] +unstuff3s: + in phase, USBIN ;[31] <- phase (one cycle too late) + andi fix, ~(1 << 3) ;[32] + nop2 ;[25] + nop2 ;[27] +bit3IsClr: + ifrset phase, USBMINUS ;[29] check phase only if D- changed + lpm ;[30] + in phase, USBIN ;[31] <- phase (one cycle too late) + ori shift, 1 << 3 ;[32] +bit4AfterClr: + mov data, fix ;[33] undo this move by swapping defines +#undef fix +#define fix x1 +#undef data +#define data x2 + ifioset USBIN, USBMINUS ;[34] <--- sample 4 + rjmp bit4IsSet ;[35] + andi shift, ~(7 << 4) ;[36] + breq unstuff4c ;[37] + in phase, USBIN ;[38] <- phase + rjmp bit5AfterClr ;[39] +unstuff4c: + in phase, USBIN ;[39] <- phase (one cycle too late) + andi fix, ~(1 << 4) ;[40] + nop2 ;[33] + nop2 ;[35] +bit4IsSet: + ifrclr phase, USBMINUS ;[37] check phase only if D- changed + lpm ;[38] + in phase, USBIN ;[39] <- phase (one cycle too late) + ori shift, 1 << 4 ;[40] +bit5AfterSet: + ser data ;[41] + ifioclr USBIN, USBMINUS ;[42] <--- sample 5 + rjmp bit5IsClr ;[43] + andi shift, ~(7 << 5) ;[44] + breq unstuff5s ;[45] + in phase, USBIN ;[46] <- phase + rjmp bit6AfterSet ;[47] +unstuff5s: + in phase, USBIN ;[47] <- phase (one cycle too late) + andi fix, ~(1 << 5) ;[48] + nop2 ;[41] + nop2 ;[43] +bit5IsClr: + ifrset phase, USBMINUS ;[45] check phase only if D- changed + lpm ;[46] + in phase, USBIN ;[47] <- phase (one cycle too late) + ori shift, 1 << 5 ;[48] +bit6AfterClr: + subi cnt, 1 ;[49] + brcs overflow ;[50] + ifioset USBIN, USBMINUS ;[51] <--- sample 6 + rjmp bit6IsSet ;[52] + andi shift, ~(3 << 6) ;[53] + cpi shift, 2 ;[54] + in phase, USBIN ;[55] <- phase + brlt unstuff6c ;[56] + rjmp bit7AfterClr ;[57] +unstuff6c: + andi fix, ~(1 << 6) ;[50] + lpm ;[51] +bit6IsSet: + ifrclr phase, USBMINUS ;[54] check phase only if D- changed + lpm ;[55] + in phase, USBIN ;[56] <- phase (one cycle too late) + ori shift, 1 << 6 ;[57] +bit7AfterSet: + ifioclr USBIN, USBMINUS ;[59] <--- sample 7 + rjmp bit7IsClr ;[60] + andi shift, ~(1 << 7) ;[61] + cpi shift, 4 ;[62] + in phase, USBIN ;[63] <- phase + brlt unstuff7s ;[64] + rjmp bit0AfterSet ;[65] -> [00] == [67] +unstuff7s: + andi fix, ~(1 << 7) ;[58] + nop ;[59] + rjmp bit7IsClr ;[60] + +macro POP_STANDARD ; 14 cycles + pop r0 + pop cnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;---------------------------------------------------------------------------- +; Transmitting data +;---------------------------------------------------------------------------- + +txByteLoop: +txBitloop: +stuffN1Delay: ; [03] + ror shift ;[-5] [11] [63] + brcc doExorN1 ;[-4] [64] + subi x3, 1 ;[-3] + brne commonN1 ;[-2] + lsl shift ;[-1] compensate ror after rjmp stuffDelay + nop ;[00] stuffing consists of just waiting 8 cycles + rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear + +sendNakAndReti: + ldi cnt, USBPID_NAK ;[-19] + rjmp sendCntAndReti ;[-18] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov r0, cnt ;[-16] + ldi YL, 0 ;[-15] R0 address is 0 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 +; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 +; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte +;uses: x1...x3, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x3 = bitstuff cnt] +;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) +usbSendAndReti: + in x2, USBDDR ;[-10] 10 cycles until SOP + ori x2, USBMASK ;[-9] + sbi USBOUT, USBMINUS ;[-8] prepare idle state; D+ and D- must have been 0 (no pullups) + out USBDDR, x2 ;[-6] <--- acquire bus + in x1, USBOUT ;[-5] port mirror for tx loop + ldi shift, 0x40 ;[-4] sync byte is first byte sent (we enter loop after ror) + ldi x2, USBMASK ;[-3] +doExorN1: + eor x1, x2 ;[-2] [06] [62] + ldi x3, 6 ;[-1] [07] [63] +commonN1: +stuffN2Delay: + out USBOUT, x1 ;[00] [08] [64] <--- set bit + ror shift ;[01] + brcc doExorN2 ;[02] + subi x3, 1 ;[03] + brne commonN2 ;[04] + lsl shift ;[05] compensate ror after rjmp stuffDelay + rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear +doExorN2: + eor x1, x2 ;[04] [12] + ldi x3, 6 ;[05] [13] +commonN2: + nop2 ;[06] [14] + subi cnt, 171 ;[08] [16] trick: (3 * 171) & 0xff = 1 + out USBOUT, x1 ;[09] [17] <--- set bit + brcs txBitloop ;[10] [27] [44] + +stuff6Delay: + ror shift ;[45] [53] + brcc doExor6 ;[46] + subi x3, 1 ;[47] + brne common6 ;[48] + lsl shift ;[49] compensate ror after rjmp stuffDelay + nop ;[50] stuffing consists of just waiting 8 cycles + rjmp stuff6Delay ;[51] after ror, C bit is reliably clear +doExor6: + eor x1, x2 ;[48] [56] + ldi x3, 6 ;[49] +common6: +stuff7Delay: + ror shift ;[50] [58] + out USBOUT, x1 ;[51] <--- set bit + brcc doExor7 ;[52] + subi x3, 1 ;[53] + brne common7 ;[54] + lsl shift ;[55] compensate ror after rjmp stuffDelay + rjmp stuff7Delay ;[56] after ror, C bit is reliably clear +doExor7: + eor x1, x2 ;[54] [62] + ldi x3, 6 ;[55] +common7: + ld shift, y+ ;[56] + nop ;[58] + tst cnt ;[59] + out USBOUT, x1 ;[60] [00]<--- set bit + brne txByteLoop ;[61] [01] +;make SE0: + cbr x1, USBMASK ;[02] prepare SE0 [spec says EOP may be 15 to 18 cycles] + lds x2, usbNewDeviceAddr;[03] + lsl x2 ;[05] we compare with left shifted address + subi YL, 2 + 0 ;[06] Only assign address on data packets, not ACK/NAK in r0 + sbci YH, 0 ;[07] + out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[01] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 0) + echo "$s\n"; + } +} + +function printBit($isAfterSet, $bitNum) +{ + ob_start(); + if($isAfterSet){ +?> + ifioclr USBIN, USBMINUS ;[00] <--- sample + rjmp bit#IsClr ;[01] + andi shift, ~(7 << #) ;[02] + breq unstuff#s ;[03] + in phase, USBIN ;[04] <- phase + rjmp bit@AfterSet ;[05] +unstuff#s: + in phase, USBIN ;[05] <- phase (one cycle too late) + andi fix, ~(1 << #) ;[06] + nop2 ;[-1] + nop2 ;[01] +bit#IsClr: + ifrset phase, USBMINUS ;[03] check phase only if D- changed + lpm ;[04] + in phase, USBIN ;[05] <- phase (one cycle too late) + ori shift, 1 << # ;[06] + + ifioset USBIN, USBMINUS ;[00] <--- sample + rjmp bit#IsSet ;[01] + andi shift, ~(7 << #) ;[02] + breq unstuff#c ;[03] + in phase, USBIN ;[04] <- phase + rjmp bit@AfterClr ;[05] +unstuff#c: + in phase, USBIN ;[05] <- phase (one cycle too late) + andi fix, ~(1 << #) ;[06] + nop2 ;[-1] + nop2 ;[01] +bit#IsSet: + ifrclr phase, USBMINUS ;[03] check phase only if D- changed + lpm ;[04] + in phase, USBIN ;[05] <- phase (one cycle too late) + ori shift, 1 << # ;[06] + +*****************************************************************************/ diff --git a/arduino code/libraries/HIDSerial/usbdrvasm15.inc b/arduino code/libraries/HIDSerial/usbdrvasm15.inc new file mode 100644 index 0000000..33bcf0e --- /dev/null +++ b/arduino code/libraries/HIDSerial/usbdrvasm15.inc @@ -0,0 +1,422 @@ +/* Name: usbdrvasm15.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: contributed by V. Bosch + * Creation Date: 2007-08-06 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 15 MHz version of the asssembler part of the USB driver. It +requires a 15 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! +*/ + +;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 15 MHz -> 10.0 cycles per bit, 80.0 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + +;---------------------------------------------------------------------------- +; order of registers pushed: +; YL, SREG [sofError] YH, shift, x1, x2, x3, bitcnt, cnt, x4 +;---------------------------------------------------------------------------- +USB_INTR_VECTOR: + push YL ;2 push only what is necessary to sync with edge ASAP + in YL, SREG ;1 + push YL ;2 +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +; +; sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +; sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +;------------------------------------------------------------------------------- +; The following code results in a sampling window of < 1/4 bit +; which meets the spec. +;------------------------------------------------------------------------------- +waitForK: ;- + sbis USBIN, USBMINUS ;1 [00] <-- sample + rjmp foundK ;2 [01] + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +;------------------------------------------------------------------------------ +; {3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for +; center sampling] +; we have 1 bit time for setup purposes, then sample again. +; Numbers in brackets are cycles from center of first sync (double K) +; bit after the instruction +;------------------------------------------------------------------------------ +foundK: ;- [02] + lds YL, usbInputBufOffset;2 [03+04] tx loop + push YH ;2 [05+06] + clr YH ;1 [07] + subi YL, lo8(-(usbRxBuf)) ;1 [08] [rx loop init] + sbci YH, hi8(-(usbRxBuf)) ;1 [09] [rx loop init] + push shift ;2 [10+11] + ser shift ;1 [12] + sbis USBIN, USBMINUS ;1 [-1] [13] <--sample:we want two bits K (sample 1 cycle too early) + rjmp haveTwoBitsK ;2 [00] [14] + pop shift ;2 [15+16] undo the push from before + pop YH ;2 [17+18] undo the push from before + rjmp waitForK ;2 [19+20] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 20 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: ;- [01] + push x1 ;2 [02+03] + push x2 ;2 [04+05] + push x3 ;2 [06+07] + push bitcnt ;2 [08+09] + in x1, USBIN ;1 [00] [10] <-- sample bit 0 + bst x1, USBMINUS ;1 [01] + bld shift, 0 ;1 [02] + push cnt ;2 [03+04] + ldi cnt, USB_BUFSIZE ;1 [05] + push x4 ;2 [06+07] tx loop + rjmp rxLoop ;2 [08] +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +unstuff0: ;- [07] (branch taken) + andi x3, ~0x01 ;1 [08] + mov x1, x2 ;1 [09] x2 contains last sampled (stuffed) bit + in x2, USBIN ;1 [00] [10] <-- sample bit 1 again + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 1 + ori shift, 0x01 ;1 [03] 0b00000001 + nop ;1 [04] + rjmp didUnstuff0 ;2 [05] +;----------------------------------------------------- +unstuff1: ;- [05] (branch taken) + mov x2, x1 ;1 [06] x1 contains last sampled (stuffed) bit + andi x3, ~0x02 ;1 [07] + ori shift, 0x02 ;1 [08] 0b00000010 + nop ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 2 again + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 2 + rjmp didUnstuff1 ;2 [03] +;----------------------------------------------------- +unstuff2: ;- [05] (branch taken) + andi x3, ~0x04 ;1 [06] + ori shift, 0x04 ;1 [07] 0b00000100 + mov x1, x2 ;1 [08] x2 contains last sampled (stuffed) bit + nop ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample bit 3 + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 3 + rjmp didUnstuff2 ;2 [03] +;----------------------------------------------------- +unstuff3: ;- [00] [10] (branch taken) + in x2, USBIN ;1 [01] [11] <-- sample stuffed bit 3 one cycle too late + andi x2, USBMASK ;1 [02] + breq se0Hop ;1 [03] SE0 check for stuffed bit 3 + andi x3, ~0x08 ;1 [04] + ori shift, 0x08 ;1 [05] 0b00001000 + rjmp didUnstuff3 ;2 [06] +;---------------------------------------------------------------------------- +; extra jobs done during bit interval: +; +; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs], +; overflow check, jump to the head of rxLoop +; bit 1: SE0 check +; bit 2: SE0 check, recovery from delay [bit 0 tasks took too long] +; bit 3: SE0 check, recovery from delay [bit 0 tasks took too long] +; bit 4: SE0 check, none +; bit 5: SE0 check, none +; bit 6: SE0 check, none +; bit 7: SE0 check, reconstruct: x3 is 0 at bit locations we changed, 1 at others +;---------------------------------------------------------------------------- +rxLoop: ;- [09] + in x2, USBIN ;1 [00] [10] <-- sample bit 1 (or possibly bit 0 stuffed) + andi x2, USBMASK ;1 [01] + brne SkipSe0Hop ;1 [02] +se0Hop: ;- [02] + rjmp se0 ;2 [03] SE0 check for bit 1 +SkipSe0Hop: ;- [03] + ser x3 ;1 [04] + andi shift, 0xf9 ;1 [05] 0b11111001 + breq unstuff0 ;1 [06] +didUnstuff0: ;- [06] + eor x1, x2 ;1 [07] + bst x1, USBMINUS ;1 [08] + bld shift, 1 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 2 (or possibly bit 1 stuffed) + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 2 + andi shift, 0xf3 ;1 [03] 0b11110011 + breq unstuff1 ;1 [04] do remaining work for bit 1 +didUnstuff1: ;- [04] + eor x2, x1 ;1 [05] + bst x2, USBMINUS ;1 [06] + bld shift, 2 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 3 (or possibly bit 2 stuffed) + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 3 + andi shift, 0xe7 ;1 [03] 0b11100111 + breq unstuff2 ;1 [04] +didUnstuff2: ;- [04] + eor x1, x2 ;1 [05] + bst x1, USBMINUS ;1 [06] + bld shift, 3 ;1 [07] +didUnstuff3: ;- [07] + andi shift, 0xcf ;1 [08] 0b11001111 + breq unstuff3 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 4 + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 4 + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 4 ;1 [05] +didUnstuff4: ;- [05] + andi shift, 0x9f ;1 [06] 0b10011111 + breq unstuff4 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 5 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 5 + eor x1, x2 ;1 [03] + bst x1, USBMINUS ;1 [04] + bld shift, 5 ;1 [05] +didUnstuff5: ;- [05] + andi shift, 0x3f ;1 [06] 0b00111111 + breq unstuff5 ;1 [07] + nop2 ;2 [08+09] + in x1, USBIN ;1 [00] [10] <-- sample bit 6 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 6 + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 6 ;1 [05] +didUnstuff6: ;- [05] + cpi shift, 0x02 ;1 [06] 0b00000010 + brlo unstuff6 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 7 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 7 + eor x1, x2 ;1 [03] + bst x1, USBMINUS ;1 [04] + bld shift, 7 ;1 [05] +didUnstuff7: ;- [05] + cpi shift, 0x04 ;1 [06] 0b00000100 + brlo unstuff7 ;1 [07] + eor x3, shift ;1 [08] reconstruct: x3 is 0 at bit locations we changed, 1 at others + nop ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 0 + st y+, x3 ;2 [01+02] store data + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 0 ;1 [05] + subi cnt, 1 ;1 [06] + brcs overflow ;1 [07] + rjmp rxLoop ;2 [08] +;----------------------------------------------------- +unstuff4: ;- [08] + andi x3, ~0x10 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 4 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 4 + ori shift, 0x10 ;1 [03] + rjmp didUnstuff4 ;2 [04] +;----------------------------------------------------- +unstuff5: ;- [08] + ori shift, 0x20 ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 5 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 5 + andi x3, ~0x20 ;1 [03] + rjmp didUnstuff5 ;2 [04] +;----------------------------------------------------- +unstuff6: ;- [08] + andi x3, ~0x40 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 6 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 6 + ori shift, 0x40 ;1 [03] + rjmp didUnstuff6 ;2 [04] +;----------------------------------------------------- +unstuff7: ;- [08] + andi x3, ~0x80 ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 7 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 7 + ori shift, 0x80 ;1 [03] + rjmp didUnstuff7 ;2 [04] + +macro POP_STANDARD ; 16 cycles + pop x4 + pop cnt + pop bitcnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;--------------------------------------------------------------------------- +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies +;--------------------------------------------------------------------------- +bitstuffN: ;- [04] + eor x1, x4 ;1 [05] + clr x2 ;1 [06] + nop ;1 [07] + rjmp didStuffN ;1 [08] +;--------------------------------------------------------------------------- +bitstuff6: ;- [04] + eor x1, x4 ;1 [05] + clr x2 ;1 [06] + rjmp didStuff6 ;1 [07] +;--------------------------------------------------------------------------- +bitstuff7: ;- [02] + eor x1, x4 ;1 [03] + clr x2 ;1 [06] + nop ;1 [05] + rjmp didStuff7 ;1 [06] +;--------------------------------------------------------------------------- +sendNakAndReti: ;- [-19] + ldi x3, USBPID_NAK ;1 [-18] + rjmp sendX3AndReti ;1 [-17] +;--------------------------------------------------------------------------- +sendAckAndReti: ;- [-17] + ldi cnt, USBPID_ACK ;1 [-16] +sendCntAndReti: ;- [-16] + mov x3, cnt ;1 [-15] +sendX3AndReti: ;- [-15] + ldi YL, 20 ;1 [-14] x3==r20 address is 20 + ldi YH, 0 ;1 [-13] + ldi cnt, 2 ;1 [-12] +; rjmp usbSendAndReti fallthrough +;--------------------------------------------------------------------------- +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +;We need not to match the transfer rate exactly because the spec demands +;only 1.5% precision anyway. +usbSendAndReti: ;- [-13] 13 cycles until SOP + in x2, USBDDR ;1 [-12] + ori x2, USBMASK ;1 [-11] + sbi USBOUT, USBMINUS ;2 [-09-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;1 [-08] port mirror for tx loop + out USBDDR, x2 ;1 [-07] <- acquire bus + ; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;1 [-06] exor mask + ldi shift, 0x80 ;1 [-05] sync byte is first byte sent + ldi bitcnt, 6 ;1 [-04] +txBitLoop: ;- [-04] [06] + sbrs shift, 0 ;1 [-03] [07] + eor x1, x4 ;1 [-02] [08] + ror shift ;1 [-01] [09] +didStuffN: ;- [09] + out USBOUT, x1 ;1 [00] [10] <-- out N + ror x2 ;1 [01] + cpi x2, 0xfc ;1 [02] + brcc bitstuffN ;1 [03] + dec bitcnt ;1 [04] + brne txBitLoop ;1 [05] + sbrs shift, 0 ;1 [06] + eor x1, x4 ;1 [07] + ror shift ;1 [08] +didStuff6: ;- [08] + nop ;1 [09] + out USBOUT, x1 ;1 [00] [10] <-- out 6 + ror x2 ;1 [01] + cpi x2, 0xfc ;1 [02] + brcc bitstuff6 ;1 [03] + sbrs shift, 0 ;1 [04] + eor x1, x4 ;1 [05] + ror shift ;1 [06] + ror x2 ;1 [07] +didStuff7: ;- [07] + ldi bitcnt, 6 ;1 [08] + cpi x2, 0xfc ;1 [09] + out USBOUT, x1 ;1 [00] [10] <-- out 7 + brcc bitstuff7 ;1 [01] + ld shift, y+ ;2 [02+03] + dec cnt ;1 [04] + brne txBitLoop ;1 [05] +makeSE0: + cbr x1, USBMASK ;1 [06] prepare SE0 [spec says EOP may be 19 to 23 cycles] + lds x2, usbNewDeviceAddr;2 [07+08] + lsl x2 ;1 [09] we compare with left shifted address +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + out USBOUT, x1 ;1 [00] [10] <-- out SE0-- from now 2 bits==20 cycl. until bus idle + subi YL, 20 + 2 ;1 [01] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;1 [02] + breq skipAddrAssign ;1 [03] + sts usbDeviceAddr, x2 ;2 [04+05] if not skipped: SE0 is one cycle longer +;---------------------------------------------------------------------------- +;end of usbDeviceAddress transfer +skipAddrAssign: ;- [03/04] + ldi x2, 1< 10.6666666 cycles per bit, 85.333333333 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt + push YL ;[-25] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-23] + push YL ;[-22] + push YH ;[-20] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-15] + rjmp foundK ;[-14] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-12] +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push bitcnt ;[-12] +; [---] ;[-11] + lds YL, usbInputBufOffset;[-10] +; [---] ;[-9] + clr YH ;[-8] + subi YL, lo8(-(usbRxBuf));[-7] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-6] [rx loop init] + push shift ;[-5] +; [---] ;[-4] + ldi bitcnt, 0x55 ;[-3] [rx loop init] + sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) + rjmp haveTwoBitsK ;[-1] + pop shift ;[0] undo the push from before + pop bitcnt ;[2] undo the push from before + rjmp waitForK ;[4] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 21 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[1] + push x2 ;[3] + push x3 ;[5] + ldi shift, 0 ;[7] + ldi x3, 1<<4 ;[8] [rx loop init] first sample is inverse bit, compensate that + push x4 ;[9] == leap + + in x1, USBIN ;[11] <-- sample bit 0 + andi x1, USBMASK ;[12] + bst x1, USBMINUS ;[13] + bld shift, 7 ;[14] + push cnt ;[15] + ldi leap, 0 ;[17] [rx loop init] + ldi cnt, USB_BUFSIZE;[18] [rx loop init] + rjmp rxbit1 ;[19] arrives at [21] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +; duration of unstuffing code should be 10.66666667 cycles. We adjust "leap" +; accordingly to approximate this value in the long run. + +unstuff6: + andi x2, USBMASK ;[03] + ori x3, 1<<6 ;[04] will not be shifted any more + andi shift, ~0x80;[05] + mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6 + subi leap, -1 ;[07] total duration = 11 bits -> subtract 1/3 + rjmp didUnstuff6 ;[08] + +unstuff7: + ori x3, 1<<7 ;[09] will not be shifted any more + in x2, USBIN ;[00] [10] re-sample bit 7 + andi x2, USBMASK ;[01] + andi shift, ~0x80;[02] + subi leap, 2 ;[03] total duration = 10 bits -> add 1/3 + rjmp didUnstuff7 ;[04] + +unstuffEven: + ori x3, 1<<6 ;[09] will be shifted right 6 times for bit 0 + in x1, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x1, USBMASK ;[02] + breq se0 ;[03] + subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 + nop2 ;[05] + rjmp didUnstuffE ;[06] + +unstuffOdd: + ori x3, 1<<5 ;[09] will be shifted right 4 times for bit 1 + in x2, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x2, USBMASK ;[02] + breq se0 ;[03] + subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 + nop2 ;[05] + rjmp didUnstuffO ;[06] + +rxByteLoop: + andi x1, USBMASK ;[03] + eor x2, x1 ;[04] + subi leap, 1 ;[05] + brpl skipLeap ;[06] + subi leap, -3 ;1 one leap cycle every 3rd byte -> 85 + 1/3 cycles per byte + nop ;1 +skipLeap: + subi x2, 1 ;[08] + ror shift ;[09] +didUnstuff6: + cpi shift, 0xfc ;[10] + in x2, USBIN ;[00] [11] <-- sample bit 7 + brcc unstuff6 ;[01] + andi x2, USBMASK ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] +didUnstuff7: + cpi shift, 0xfc ;[06] + brcc unstuff7 ;[07] + eor x3, shift ;[08] reconstruct: x3 is 1 at bit locations we changed, 0 at others + st y+, x3 ;[09] store data +rxBitLoop: + in x1, USBIN ;[00] [11] <-- sample bit 0/2/4 + andi x1, USBMASK ;[01] + eor x2, x1 ;[02] + andi x3, 0x3f ;[03] topmost two bits reserved for 6 and 7 + subi x2, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffEven ;[07] +didUnstuffE: + lsr x3 ;[08] + lsr x3 ;[09] +rxbit1: + in x2, USBIN ;[00] [10] <-- sample bit 1/3/5 + andi x2, USBMASK ;[01] + breq se0 ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffOdd ;[07] +didUnstuffO: + subi bitcnt, 0xab;[08] == addi 0x55, 0x55 = 0x100/3 + brcs rxBitLoop ;[09] + + subi cnt, 1 ;[10] + in x1, USBIN ;[00] [11] <-- sample bit 6 + brcc rxByteLoop ;[01] + rjmp overflow + +macro POP_STANDARD ; 14 cycles + pop cnt + pop x4 + pop x3 + pop x2 + pop x1 + pop shift + pop bitcnt + endm +macro POP_RETI ; 7 cycles + pop YH + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies + +bitstuffN: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] + nop2 ;[7] + nop ;[9] + out USBOUT, x1 ;[10] <-- out + rjmp didStuffN ;[0] + +bitstuff6: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] Carry is zero due to brcc + rol shift ;[7] compensate for ror shift at branch destination + rjmp didStuff6 ;[8] + +bitstuff7: + ldi x2, 0 ;[2] Carry is zero due to brcc + rjmp didStuff7 ;[3] + + +sendNakAndReti: + ldi x3, USBPID_NAK ;[-18] + rjmp sendX3AndReti ;[-17] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov x3, cnt ;[-16] +sendX3AndReti: + ldi YL, 20 ;[-15] x3==r20 address is 20 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +;We don't match the transfer rate exactly (don't insert leap cycles every third +;byte) because the spec demands only 1.5% precision anyway. +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-6] exor mask + ldi shift, 0x80 ;[-5] sync byte is first byte sent +txByteLoop: + ldi bitcnt, 0x35 ;[-4] [6] binary 0011 0101 +txBitLoop: + sbrs shift, 0 ;[-3] [7] + eor x1, x4 ;[-2] [8] + out USBOUT, x1 ;[-1] [9] <-- out N + ror shift ;[0] [10] + ror x2 ;[1] +didStuffN: + cpi x2, 0xfc ;[2] + brcc bitstuffN ;[3] + lsr bitcnt ;[4] + brcc txBitLoop ;[5] + brne txBitLoop ;[6] + + sbrs shift, 0 ;[7] + eor x1, x4 ;[8] +didStuff6: + out USBOUT, x1 ;[-1] [9] <-- out 6 + ror shift ;[0] [10] + ror x2 ;[1] + cpi x2, 0xfc ;[2] + brcc bitstuff6 ;[3] + ror shift ;[4] +didStuff7: + ror x2 ;[5] + sbrs x2, 7 ;[6] + eor x1, x4 ;[7] + nop ;[8] + cpi x2, 0xfc ;[9] + out USBOUT, x1 ;[-1][10] <-- out 7 + brcc bitstuff7 ;[0] [11] + ld shift, y+ ;[1] + dec cnt ;[3] + brne txByteLoop ;[4] +;make SE0: + cbr x1, USBMASK ;[5] prepare SE0 [spec says EOP may be 21 to 25 cycles] + lds x2, usbNewDeviceAddr;[6] + lsl x2 ;[8] we compare with left shifted address + subi YL, 20 + 2 ;[9] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[10] + out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[0] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< max 52 cycles interrupt disable +;max stack usage: [ret(2), r0, SREG, YL, YH, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 16.5 MHz -> 11 cycles per bit +; 16.3125 MHz < F_CPU < 16.6875 MHz (+/- 1.1%) +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], r0, YH, shift, x1, x2, x3, x4, cnt + push YL ;[-23] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-21] + push YL ;[-20] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-15] + rjmp foundK ;[-14] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-12] +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push r0 ;[-12] +; [---] ;[-11] + push YH ;[-10] +; [---] ;[-9] + lds YL, usbInputBufOffset;[-8] +; [---] ;[-7] + clr YH ;[-6] + subi YL, lo8(-(usbRxBuf));[-5] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-4] [rx loop init] + mov r0, x2 ;[-3] [rx loop init] + sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) + rjmp haveTwoBitsK ;[-1] + pop YH ;[0] undo the pushes from before + pop r0 ;[2] + rjmp waitForK ;[4] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 22 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: ;[1] + push shift ;[1] + push x1 ;[3] + push x2 ;[5] + push x3 ;[7] + ldi shift, 0xff ;[9] [rx loop init] + ori x3, 0xff ;[10] [rx loop init] == ser x3, clear zero flag + + in x1, USBIN ;[11] <-- sample bit 0 + bst x1, USBMINUS ;[12] + bld shift, 0 ;[13] + push x4 ;[14] == phase +; [---] ;[15] + push cnt ;[16] +; [---] ;[17] + ldi phase, 0 ;[18] [rx loop init] + ldi cnt, USB_BUFSIZE;[19] [rx loop init] + rjmp rxbit1 ;[20] +; [---] ;[21] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +/* +byte oriented operations done during loop: +bit 0: store data +bit 1: SE0 check +bit 2: overflow check +bit 3: catch up +bit 4: rjmp to achieve conditional jump range +bit 5: PLL +bit 6: catch up +bit 7: jump, fixup bitstuff +; 87 [+ 2] cycles +------------------------------------------------------------------ +*/ +continueWithBit5: + in x2, USBIN ;[055] <-- bit 5 + eor r0, x2 ;[056] + or phase, r0 ;[057] + sbrc phase, USBMINUS ;[058] + lpm ;[059] optional nop3; modifies r0 + in phase, USBIN ;[060] <-- phase + eor x1, x2 ;[061] + bst x1, USBMINUS ;[062] + bld shift, 5 ;[063] + andi shift, 0x3f ;[064] + in x1, USBIN ;[065] <-- bit 6 + breq unstuff5 ;[066] *** unstuff escape + eor phase, x1 ;[067] + eor x2, x1 ;[068] + bst x2, USBMINUS ;[069] + bld shift, 6 ;[070] +didUnstuff6: ;[ ] + in r0, USBIN ;[071] <-- phase + cpi shift, 0x02 ;[072] + brlo unstuff6 ;[073] *** unstuff escape +didUnstuff5: ;[ ] + nop2 ;[074] +; [---] ;[075] + in x2, USBIN ;[076] <-- bit 7 + eor x1, x2 ;[077] + bst x1, USBMINUS ;[078] + bld shift, 7 ;[079] +didUnstuff7: ;[ ] + eor r0, x2 ;[080] + or phase, r0 ;[081] + in r0, USBIN ;[082] <-- phase + cpi shift, 0x04 ;[083] + brsh rxLoop ;[084] +; [---] ;[085] +unstuff7: ;[ ] + andi x3, ~0x80 ;[085] + ori shift, 0x80 ;[086] + in x2, USBIN ;[087] <-- sample stuffed bit 7 + nop ;[088] + rjmp didUnstuff7 ;[089] +; [---] ;[090] + ;[080] + +unstuff5: ;[067] + eor phase, x1 ;[068] + andi x3, ~0x20 ;[069] + ori shift, 0x20 ;[070] + in r0, USBIN ;[071] <-- phase + mov x2, x1 ;[072] + nop ;[073] + nop2 ;[074] +; [---] ;[075] + in x1, USBIN ;[076] <-- bit 6 + eor r0, x1 ;[077] + or phase, r0 ;[078] + eor x2, x1 ;[079] + bst x2, USBMINUS ;[080] + bld shift, 6 ;[081] no need to check bitstuffing, we just had one + in r0, USBIN ;[082] <-- phase + rjmp didUnstuff5 ;[083] +; [---] ;[084] + ;[074] + +unstuff6: ;[074] + andi x3, ~0x40 ;[075] + in x1, USBIN ;[076] <-- bit 6 again + ori shift, 0x40 ;[077] + nop2 ;[078] +; [---] ;[079] + rjmp didUnstuff6 ;[080] +; [---] ;[081] + ;[071] + +unstuff0: ;[013] + eor r0, x2 ;[014] + or phase, r0 ;[015] + andi x2, USBMASK ;[016] check for SE0 + in r0, USBIN ;[017] <-- phase + breq didUnstuff0 ;[018] direct jump to se0 would be too long + andi x3, ~0x01 ;[019] + ori shift, 0x01 ;[020] + mov x1, x2 ;[021] mov existing sample + in x2, USBIN ;[022] <-- bit 1 again + rjmp didUnstuff0 ;[023] +; [---] ;[024] + ;[014] + +unstuff1: ;[024] + eor r0, x1 ;[025] + or phase, r0 ;[026] + andi x3, ~0x02 ;[027] + in r0, USBIN ;[028] <-- phase + ori shift, 0x02 ;[029] + mov x2, x1 ;[030] + rjmp didUnstuff1 ;[031] +; [---] ;[032] + ;[022] + +unstuff2: ;[035] + eor r0, x2 ;[036] + or phase, r0 ;[037] + andi x3, ~0x04 ;[038] + in r0, USBIN ;[039] <-- phase + ori shift, 0x04 ;[040] + mov x1, x2 ;[041] + rjmp didUnstuff2 ;[042] +; [---] ;[043] + ;[033] + +unstuff3: ;[043] + in x2, USBIN ;[044] <-- bit 3 again + eor r0, x2 ;[045] + or phase, r0 ;[046] + andi x3, ~0x08 ;[047] + ori shift, 0x08 ;[048] + nop ;[049] + in r0, USBIN ;[050] <-- phase + rjmp didUnstuff3 ;[051] +; [---] ;[052] + ;[042] + +unstuff4: ;[053] + andi x3, ~0x10 ;[054] + in x1, USBIN ;[055] <-- bit 4 again + ori shift, 0x10 ;[056] + rjmp didUnstuff4 ;[057] +; [---] ;[058] + ;[048] + +rxLoop: ;[085] + eor x3, shift ;[086] reconstruct: x3 is 0 at bit locations we changed, 1 at others + in x1, USBIN ;[000] <-- bit 0 + st y+, x3 ;[001] +; [---] ;[002] + eor r0, x1 ;[003] + or phase, r0 ;[004] + eor x2, x1 ;[005] + in r0, USBIN ;[006] <-- phase + ser x3 ;[007] + bst x2, USBMINUS ;[008] + bld shift, 0 ;[009] + andi shift, 0xf9 ;[010] +rxbit1: ;[ ] + in x2, USBIN ;[011] <-- bit 1 + breq unstuff0 ;[012] *** unstuff escape + andi x2, USBMASK ;[013] SE0 check for bit 1 +didUnstuff0: ;[ ] Z only set if we detected SE0 in bitstuff + breq se0 ;[014] + eor r0, x2 ;[015] + or phase, r0 ;[016] + in r0, USBIN ;[017] <-- phase + eor x1, x2 ;[018] + bst x1, USBMINUS ;[019] + bld shift, 1 ;[020] + andi shift, 0xf3 ;[021] +didUnstuff1: ;[ ] + in x1, USBIN ;[022] <-- bit 2 + breq unstuff1 ;[023] *** unstuff escape + eor r0, x1 ;[024] + or phase, r0 ;[025] + subi cnt, 1 ;[026] overflow check + brcs overflow ;[027] + in r0, USBIN ;[028] <-- phase + eor x2, x1 ;[029] + bst x2, USBMINUS ;[030] + bld shift, 2 ;[031] + andi shift, 0xe7 ;[032] +didUnstuff2: ;[ ] + in x2, USBIN ;[033] <-- bit 3 + breq unstuff2 ;[034] *** unstuff escape + eor r0, x2 ;[035] + or phase, r0 ;[036] + eor x1, x2 ;[037] + bst x1, USBMINUS ;[038] + in r0, USBIN ;[039] <-- phase + bld shift, 3 ;[040] + andi shift, 0xcf ;[041] +didUnstuff3: ;[ ] + breq unstuff3 ;[042] *** unstuff escape + nop ;[043] + in x1, USBIN ;[044] <-- bit 4 + eor x2, x1 ;[045] + bst x2, USBMINUS ;[046] + bld shift, 4 ;[047] +didUnstuff4: ;[ ] + eor r0, x1 ;[048] + or phase, r0 ;[049] + in r0, USBIN ;[050] <-- phase + andi shift, 0x9f ;[051] + breq unstuff4 ;[052] *** unstuff escape + rjmp continueWithBit5;[053] +; [---] ;[054] + +macro POP_STANDARD ; 16 cycles + pop cnt + pop x4 + pop x3 + pop x2 + pop x1 + pop shift + pop YH + pop r0 + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies + +bitstuff7: + eor x1, x4 ;[4] + ldi x2, 0 ;[5] + nop2 ;[6] C is zero (brcc) + rjmp didStuff7 ;[8] + +bitstuffN: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] + lpm ;[7] 3 cycle NOP, modifies r0 + out USBOUT, x1 ;[10] <-- out + rjmp didStuffN ;[0] + +#define bitStatus x3 + +sendNakAndReti: + ldi cnt, USBPID_NAK ;[-19] + rjmp sendCntAndReti ;[-18] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov r0, cnt ;[-16] + ldi YL, 0 ;[-15] R0 address is 0 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-6] exor mask + ldi shift, 0x80 ;[-5] sync byte is first byte sent + ldi bitStatus, 0xff ;[-4] init bit loop counter, works for up to 12 bytes +byteloop: +bitloop: + sbrs shift, 0 ;[8] [-3] + eor x1, x4 ;[9] [-2] + out USBOUT, x1 ;[10] [-1] <-- out + ror shift ;[0] + ror x2 ;[1] +didStuffN: + cpi x2, 0xfc ;[2] + brcc bitstuffN ;[3] + nop ;[4] + subi bitStatus, 37 ;[5] 256 / 7 ~=~ 37 + brcc bitloop ;[6] when we leave the loop, bitStatus has almost the initial value + sbrs shift, 0 ;[7] + eor x1, x4 ;[8] + ror shift ;[9] +didStuff7: + out USBOUT, x1 ;[10] <-- out + ror x2 ;[0] + cpi x2, 0xfc ;[1] + brcc bitstuff7 ;[2] + ld shift, y+ ;[3] + dec cnt ;[5] + brne byteloop ;[6] +;make SE0: + cbr x1, USBMASK ;[7] prepare SE0 [spec says EOP may be 21 to 25 cycles] + lds x2, usbNewDeviceAddr;[8] + lsl x2 ;[10] we compare with left shifted address + out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + subi YL, 2 ;[0] Only assign address on data packets, not ACK/NAK in r0 + sbci YH, 0 ;[1] + breq skipAddrAssign ;[2] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 12 cycles per bit +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts +;register use in receive loop to receive the data bytes: +; shift assembles the byte currently being received +; x1 holds the D+ and D- line state +; x2 holds the previous line state +; cnt holds the number of bytes left in the receive buffer +; x3 holds the higher crc byte (see algorithm below) +; x4 is used as temporary register for the crc algorithm +; x5 is used for unstuffing: when unstuffing the last received bit is inverted in shift (to prevent further +; unstuffing calls. In the same time the corresponding bit in x5 is cleared to mark the bit as beening iverted +; zl lower crc value and crc table index +; zh used for crc table accesses + +;-------------------------------------------------------------------------------------------------------------- +; CRC mods: +; table driven crc checker, Z points to table in prog space +; ZL is the lower crc byte, x3 is the higher crc byte +; x4 is used as temp register to store different results +; the initialization of the crc register is not 0xFFFF but 0xFE54. This is because during the receipt of the +; first data byte an virtual zero data byte is added to the crc register, this results in the correct initial +; value of 0xFFFF at beginning of the second data byte before the first data byte is added to the crc. +; The magic number 0xFE54 results form the crc table: At tabH[0x54] = 0xFF = crcH (required) and +; tabL[0x54] = 0x01 -> crcL = 0x01 xor 0xFE = 0xFF +; bitcnt is renamed to x5 and is used for unstuffing purposes, the unstuffing works like in the 12MHz version +;-------------------------------------------------------------------------------------------------------------- +; CRC algorithm: +; The crc register is formed by x3 (higher byte) and ZL (lower byte). The algorithm uses a 'reversed' form +; i.e. that it takes the least significant bit first and shifts to the right. So in fact the highest order +; bit seen from the polynomial devision point of view is the lsb of ZL. (If this sounds strange to you i +; propose a research on CRC :-) ) +; Each data byte received is xored to ZL, the lower crc byte. This byte now builds the crc +; table index. Next the new high byte is loaded from the table and stored in x4 until we have space in x3 +; (its destination). +; Afterwards the lower table is loaded from the table and stored in ZL (the old index is overwritten as +; we don't need it anymore. In fact this is a right shift by 8 bits.) Now the old crc high value is xored +; to ZL, this is the second shift of the old crc value. Now x4 (the temp reg) is moved to x3 and the crc +; calculation is done. +; Prior to the first byte the two CRC register have to be initialized to 0xFFFF (as defined in usb spec) +; however the crc engine also runs during the receipt of the first byte, therefore x3 and zl are initialized +; to a magic number which results in a crc value of 0xFFFF after the first complete byte. +; +; This algorithm is split into the extra cycles of the different bits: +; bit7: XOR the received byte to ZL +; bit5: load the new high byte to x4 +; bit6: load the lower xor byte from the table, xor zl and x3, store result in zl (=the new crc low value) +; move x4 (the new high byte) to x3, the crc value is ready +; + + +macro POP_STANDARD ; 18 cycles + pop ZH + pop ZL + pop cnt + pop x5 + pop x3 + pop x2 + pop x1 + pop shift + pop x4 + endm +macro POP_RETI ; 7 cycles + pop YH + pop YL + out SREG, YL + pop YL + endm + +macro CRC_CLEANUP_AND_CHECK + ; the last byte has already been xored with the lower crc byte, we have to do the table lookup and xor + ; x3 is the higher crc byte, zl the lower one + ldi ZH, hi8(usbCrcTableHigh);[+1] get the new high byte from the table + lpm x2, Z ;[+2][+3][+4] + ldi ZH, hi8(usbCrcTableLow);[+5] get the new low xor byte from the table + lpm ZL, Z ;[+6][+7][+8] + eor ZL, x3 ;[+7] xor the old high byte with the value from the table, x2:ZL now holds the crc value + cpi ZL, 0x01 ;[+8] if the crc is ok we have a fixed remainder value of 0xb001 in x2:ZL (see usb spec) + brne ignorePacket ;[+9] detected a crc fault -> paket is ignored and retransmitted by the host + cpi x2, 0xb0 ;[+10] + brne ignorePacket ;[+11] detected a crc fault -> paket is ignored and retransmitted by the host + endm + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG, YH, [sofError], x4, shift, x1, x2, x3, x5, cnt, ZL, ZH + push YL ;[-28] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-26] + push YL ;[-25] + push YH ;[-23] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-17] + rjmp foundK ;[-16] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-15] +;{3, 5} after falling D- edge, average delay: 4 cycles +;bit0 should be at 30 (2.5 bits) for center sampling. Currently at 4 so 26 cylces till bit 0 sample +;use 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push x4 ;[-14] +; [---] ;[-13] + lds YL, usbInputBufOffset;[-12] used to toggle the two usb receive buffers +; [---] ;[-11] + clr YH ;[-10] + subi YL, lo8(-(usbRxBuf));[-9] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-8] [rx loop init] + push shift ;[-7] +; [---] ;[-6] + ldi shift, 0x80 ;[-5] the last bit is the end of byte marker for the pid receiver loop + clc ;[-4] the carry has to be clear for receipt of pid bit 0 + sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) + rjmp haveTwoBitsK ;[-2] + pop shift ;[-1] undo the push from before + pop x4 ;[1] + rjmp waitForK ;[3] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 24 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[0] + push x2 ;[2] + push x3 ;[4] crc high byte + ldi x2, 1< jump back and store the byte + ori shift, 0x01 ;[11] invert the last received bit to prevent furhter unstuffing + in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + andi x5, 0xFE ;[1] mark this bit as inverted (will be corrected before storing shift) + eor x1, x2 ;[2] x1 and x2 have to be different because the stuff bit is always a zero + andi x1, USBMASK ;[3] mask the interesting bits + breq stuffErr ;[4] if the stuff bit is a 1-bit something went wrong + mov x1, x2 ;[5] the next bit expects the last state to be in x1 + rjmp didunstuff0 ;[6] + ;[7] jump delay of rjmp didunstuffX + +unstuff1: ;[11] this is the jump delay of breq unstuffX + in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + ori shift, 0x02 ;[1] invert the last received bit to prevent furhter unstuffing + andi x5, 0xFD ;[2] mark this bit as inverted (will be corrected before storing shift) + eor x2, x1 ;[3] x1 and x2 have to be different because the stuff bit is always a zero + andi x2, USBMASK ;[4] mask the interesting bits + breq stuffErr ;[5] if the stuff bit is a 1-bit something went wrong + mov x2, x1 ;[6] the next bit expects the last state to be in x2 + nop2 ;[7] + ;[8] + rjmp didunstuff1 ;[9] + ;[10] jump delay of rjmp didunstuffX + +unstuff2: ;[9] this is the jump delay of breq unstuffX + ori shift, 0x04 ;[10] invert the last received bit to prevent furhter unstuffing + andi x5, 0xFB ;[11] mark this bit as inverted (will be corrected before storing shift) + in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero + andi x1, USBMASK ;[2] mask the interesting bits + breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong + mov x1, x2 ;[4] the next bit expects the last state to be in x1 + nop2 ;[5] + ;[6] + rjmp didunstuff2 ;[7] + ;[8] jump delay of rjmp didunstuffX + +unstuff3: ;[9] this is the jump delay of breq unstuffX + ori shift, 0x08 ;[10] invert the last received bit to prevent furhter unstuffing + andi x5, 0xF7 ;[11] mark this bit as inverted (will be corrected before storing shift) + in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero + andi x2, USBMASK ;[2] mask the interesting bits + breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong + mov x2, x1 ;[4] the next bit expects the last state to be in x2 + nop2 ;[5] + ;[6] + rjmp didunstuff3 ;[7] + ;[8] jump delay of rjmp didunstuffX + + + +; the include has to be here due to branch distance restirctions +#define __USE_CRC__ +#include "asmcommon.inc" + + + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies +; 7.5 bit times is 90 cycles. ...there is plenty of time + + +sendNakAndReti: + ldi x3, USBPID_NAK ;[-18] + rjmp sendX3AndReti ;[-17] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov x3, cnt ;[-16] +sendX3AndReti: + ldi YL, 20 ;[-15] x3==r20 address is 20 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent + +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-6] <- acquire bus + ldi x2, 0 ;[-6] init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-5] exor mask + ldi shift, 0x80 ;[-4] sync byte is first byte sent +txByteLoop: + ldi bitcnt, 0x40 ;[-3]=[9] binary 01000000 +txBitLoop: ; the loop sends the first 7 bits of the byte + sbrs shift, 0 ;[-2]=[10] if we have to send a 1 don't change the line state + eor x1, x4 ;[-1]=[11] + out USBOUT, x1 ;[0] + ror shift ;[1] + ror x2 ;[2] transfers the last sent bit to the stuffing history +didStuffN: + nop ;[3] + nop ;[4] + cpi x2, 0xfc ;[5] if we sent six consecutive ones + brcc bitstuffN ;[6] + lsr bitcnt ;[7] + brne txBitLoop ;[8] restart the loop while the 1 is still in the bitcount + +; transmit bit 7 + sbrs shift, 0 ;[9] + eor x1, x4 ;[10] +didStuff7: + ror shift ;[11] + out USBOUT, x1 ;[0] transfer bit 7 to the pins + ror x2 ;[1] move the bit into the stuffing history + cpi x2, 0xfc ;[2] + brcc bitstuff7 ;[3] + ld shift, y+ ;[4] get next byte to transmit + dec cnt ;[5] decrement byte counter + brne txByteLoop ;[7] if we have more bytes start next one + ;[8] branch delay + +;make SE0: + cbr x1, USBMASK ;[8] prepare SE0 [spec says EOP may be 25 to 30 cycles] + lds x2, usbNewDeviceAddr;[9] + lsl x2 ;[11] we compare with left shifted address + out USBOUT, x1 ;[0] <-- out SE0 -- from now 2 bits = 24 cycles until bus idle + subi YL, 20 + 2 ;[1] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[2] +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[3] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< +int main (int argc, char **argv) +{ + int i, j; + for (i=0; i<512; i++){ + unsigned short crc = i & 0xff; + for(j=0; j<8; j++) crc = (crc >> 1) ^ ((crc & 1) ? 0xa001 : 0); + if((i & 7) == 0) printf("\n.byte "); + printf("0x%02x, ", (i > 0xff ? (crc >> 8) : crc) & 0xff); + if(i == 255) printf("\n"); + } + return 0; +} + +// Use the following algorithm to compute CRC values: +ushort computeCrc(uchar *msg, uchar msgLen) +{ + uchar i; + ushort crc = 0xffff; + for(i = 0; i < msgLen; i++) + crc = usbCrcTable16[lo8(crc) ^ msg[i]] ^ hi8(crc); + return crc; +} +*/ + +.balign 256 +usbCrcTableLow: +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 + +; .balign 256 +usbCrcTableHigh: +.byte 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2 +.byte 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04 +.byte 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E +.byte 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8 +.byte 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A +.byte 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC +.byte 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6 +.byte 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10 +.byte 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32 +.byte 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4 +.byte 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE +.byte 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38 +.byte 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA +.byte 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C +.byte 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26 +.byte 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0 +.byte 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62 +.byte 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4 +.byte 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE +.byte 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68 +.byte 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA +.byte 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C +.byte 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76 +.byte 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0 +.byte 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92 +.byte 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54 +.byte 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E +.byte 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98 +.byte 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A +.byte 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C +.byte 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86 +.byte 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 + diff --git a/arduino code/libraries/HIDSerial/usbdrvasm20.inc b/arduino code/libraries/HIDSerial/usbdrvasm20.inc new file mode 100644 index 0000000..5027edd --- /dev/null +++ b/arduino code/libraries/HIDSerial/usbdrvasm20.inc @@ -0,0 +1,359 @@ +/* Name: usbdrvasm20.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Jeroen Benschop + * Based on usbdrvasm16.inc from Christian Starkjohann + * Creation Date: 2008-03-05 + * Tabsize: 4 + * Copyright: (c) 2008 by Jeroen Benschop and OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 20 MHz version of the asssembler part of the USB driver. It +requires a 20 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! +*/ + +#define leap2 x3 +#ifdef __IAR_SYSTEMS_ASM__ +#define nextInst $+2 +#else +#define nextInst .+0 +#endif + +;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 20 MHz -> 13.333333 cycles per bit, 106.666667 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts +;register use in receive loop: +; shift assembles the byte currently being received +; x1 holds the D+ and D- line state +; x2 holds the previous line state +; x4 (leap) is used to add a leap cycle once every three bytes received +; X3 (leap2) is used to add a leap cycle once every three stuff bits received +; bitcnt is used to determine when a stuff bit is due +; cnt holds the number of bytes left in the receive buffer + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt + push YL ;[-28] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-26] + push YL ;[-25] + push YH ;[-23] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-19] + rjmp foundK ;[-18] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-16] +;{3, 5} after falling D- edge, average delay: 4 cycles +;bit0 should be at 34 for center sampling. Currently at 4 so 30 cylces till bit 0 sample +;use 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push bitcnt ;[-16] +; [---] ;[-15] + lds YL, usbInputBufOffset;[-14] +; [---] ;[-13] + clr YH ;[-12] + subi YL, lo8(-(usbRxBuf));[-11] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init] + push shift ;[-9] +; [---] ;[-8] + ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected + nop2 ;[-6] +; [---] ;[-5] + ldi bitcnt, 5 ;[-4] [rx loop init] + sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) + rjmp haveTwoBitsK ;[-2] + pop shift ;[-1] undo the push from before + pop bitcnt ;[1] + rjmp waitForK ;[3] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 27 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[0] + push x2 ;[2] + push x3 ;[4] (leap2) + ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit + push x4 ;[7] == leap + ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received + push cnt ;[10] + ldi cnt, USB_BUFSIZE ;[12] [rx loop init] + ldi x2, 1< +#ifndef __IAR_SYSTEMS_ASM__ +# include +#endif + +#define __attribute__(arg) /* not supported on IAR */ + +#ifdef __IAR_SYSTEMS_ASM__ +# define __ASSEMBLER__ /* IAR does not define standard macro for asm */ +#endif + +#ifdef __HAS_ELPM__ +# define PROGMEM __farflash +#else +# define PROGMEM __flash +#endif + +#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) + +/* The following definitions are not needed by the driver, but may be of some + * help if you port a gcc based project to IAR. + */ +#define cli() __disable_interrupt() +#define sei() __enable_interrupt() +#define wdt_reset() __watchdog_reset() +#define _BV(x) (1 << (x)) + +/* assembler compatibility macros */ +#define nop2 rjmp $+2 /* jump to next instruction */ +#define XL r26 +#define XH r27 +#define YL r28 +#define YH r29 +#define ZL r30 +#define ZH r31 +#define lo8(x) LOW(x) +#define hi8(x) (((x)>>8) & 0xff) /* not HIGH to allow XLINK to make a proper range check */ + +/* Depending on the device you use, you may get problems with the way usbdrv.h + * handles the differences between devices. Since IAR does not use #defines + * for MCU registers, we can't check for the existence of a particular + * register with an #ifdef. If the autodetection mechanism fails, include + * definitions for the required USB_INTR_* macros in your usbconfig.h. See + * usbconfig-prototype.h and usbdrv.h for details. + */ + +/* ------------------------------------------------------------------------- */ +#elif __CODEVISIONAVR__ /* check for CodeVision AVR */ +/* ------------------------------------------------------------------------- */ +/* This port is not working (yet) */ + +/* #define F_CPU _MCU_CLOCK_FREQUENCY_ seems to be defined automatically */ + +#include +#include + +#define __attribute__(arg) /* not supported on IAR */ + +#define PROGMEM __flash +#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) + +#ifndef __ASSEMBLER__ +static inline void cli(void) +{ + #asm("cli"); +} +static inline void sei(void) +{ + #asm("sei"); +} +#endif +#define _delay_ms(t) delay_ms(t) +#define _BV(x) (1 << (x)) +#define USB_CFG_USE_SWITCH_STATEMENT 1 /* macro for if() cascase fails for unknown reason */ + +#define macro .macro +#define endm .endmacro +#define nop2 rjmp .+0 /* jump to next instruction */ + +/* ------------------------------------------------------------------------- */ +#else /* default development environment is avr-gcc/avr-libc */ +/* ------------------------------------------------------------------------- */ + +#include +#ifdef __ASSEMBLER__ +# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ +#else +# include +#endif + +#if USB_CFG_DRIVER_FLASH_PAGE +# define USB_READ_FLASH(addr) pgm_read_byte_far(((long)USB_CFG_DRIVER_FLASH_PAGE << 16) | (long)(addr)) +#else +# define USB_READ_FLASH(addr) pgm_read_byte(addr) +#endif + +#define macro .macro +#define endm .endm +#define nop2 rjmp .+0 /* jump to next instruction */ + +#endif /* development environment */ + +/* for conveniecne, ensure that PRG_RDB exists */ +#ifndef PRG_RDB +# define PRG_RDB(addr) USB_READ_FLASH(addr) +#endif +#endif /* __usbportability_h_INCLUDED__ */ diff --git a/bootloader/Makefile b/bootloader/Makefile new file mode 100644 index 0000000..126f37c --- /dev/null +++ b/bootloader/Makefile @@ -0,0 +1,81 @@ +# 16k bootloader addr = 0x3800 +# 8k bootloader addr = 0x1800 +# 32k bootloader addr for 4k bootloader = 0x7000 +# 32k bootloader addr for 2k bootloader = 0x7800 + +F_CPU = 12000000 +DEVICE = atmega328p +BOOTLOADER_ADDRESS = 0x7000 + +#Fuses +#atmega8: -U lfuse:w:0x7F:m -U hfuse:w:0xD8:m -U lock:w:0xCF:m +#atmega88: -U lfuse:w:0xFF:m -U hfuse:w:0xDF:m -U efuse:w:0xF8:m -U lock:w:0xCF:m +#atmega88p: -U lfuse:w:0xFF:m -U hfuse:w:0xDF:m -U efuse:w:0xF8:m -U lock:w:0xCF:m +#atmega168: -U lfuse:w:0xFF:m -U hfuse:w:0xDF:m -U efuse:w:0xF8:m -U lock:w:0xCF:m +#atmega168p: -U lfuse:w:0xFF:m -U hfuse:w:0xDF:m -U efuse:w:0xF8:m -U lock:w:0xCF:m +#atmega328p (4k bootloader): -U lfuse:w:0xFF:m -U hfuse:w:0xD8:m -U efuse:w:0xFF:m -U lock:w:0x0F:m +#atmega328p (2k bootloader): -U lfuse:w:0xFF:m -U hfuse:w:0xDA:m -U efuse:w:0xFF:m -U lock:w:0xCF:m + +CC = avr-gcc + +# Options: +DEFINES = #-DDEBUG_LEVEL=2 +CFLAGS = -Wall -Os -I. -mmcu=$(DEVICE) -DF_CPU=$(F_CPU) $(DEFINES) +LDFLAGS = -Wl,--section-start=.text=$(BOOTLOADER_ADDRESS) +FUSEBITS = -U lfuse:w:0xFF:m -U hfuse:w:0xD8:m -U efuse:w:0x05:m -U lock:w:0x0F:m + +OBJECTS = usbdrv/usbdrvasm.o main.o #usbdrv/oddebug.o + +# symbolic targets: +all: main.hex + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +.S.o: + $(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@ + +.c.s: + $(CC) $(CFLAGS) -S $< -o $@ + +fuse: + avrdude -c usbasp -p m328p -F $(FUSEBITS) + +flash: + avrdude -c usbasp -p m328p -F -B 1 -U flash:w:main.hex + +clean: + rm -f main.hex main.bin *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s + +main.bin: $(OBJECTS) + $(CC) $(CFLAGS) -o main.bin $(OBJECTS) $(LDFLAGS) + +main.hex: main.bin + avr-objcopy -j .text -j .data -O ihex main.bin main.hex + avr-size main.hex + +disasm: main.bin + avr-objdump -d main.bin + +cpp: + $(CC) $(CFLAGS) -E main.c + +# Special rules for generating hex files for various devices and clock speeds +ALLHEXFILES = hexfiles/mega8_12mhz.hex hexfiles/mega8_15mhz.hex hexfiles/mega8_16mhz.hex \ + hexfiles/mega88_12mhz.hex hexfiles/mega88_15mhz.hex hexfiles/mega88_16mhz.hex hexfiles/mega88_20mhz.hex\ + hexfiles/mega168_12mhz.hex hexfiles/mega168_15mhz.hex hexfiles/mega168_16mhz.hex hexfiles/mega168_20mhz.hex + +allhexfiles: $(ALLHEXFILES) + $(MAKE) clean + avr-size hexfiles/*.hex + avr-size hexfiles/*.hex >> results.txt + +$(ALLHEXFILES): + @[ -d hexfiles ] || mkdir hexfiles + @device=`echo $@ | sed -e 's|.*/mega||g' -e 's|_.*||g'`; \ + clock=`echo $@ | sed -e 's|.*_||g' -e 's|mhz.*||g'`; \ + addr=`echo $$device | sed -e 's/\([0-9]\)8/\1/g' | awk '{printf("%x", ($$1 - 2) * 1024)}'`; \ + echo "### Make with F_CPU=$${clock}000000 DEVICE=atmega$$device BOOTLOADER_ADDRESS=$$addr"; \ + $(MAKE) clean; \ + $(MAKE) main.hex F_CPU=$${clock}000000 DEVICE=atmega$$device BOOTLOADER_ADDRESS=$$addr DEFINES=-DUSE_AUTOCONFIG=1 + mv main.hex $@ diff --git a/bootloader/bootloaderconfig.h b/bootloader/bootloaderconfig.h new file mode 100644 index 0000000..4a371a1 --- /dev/null +++ b/bootloader/bootloaderconfig.h @@ -0,0 +1,164 @@ +/* Name: bootloaderconfig.h + * Project: USBaspLoader + * Author: Christian Starkjohann + * Modified by: Frank Zhao + * Creation Date: 2007-12-08 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) + * This Revision: $Id: bootloaderconfig.h 729 2009-03-20 09:03:58Z cs $ + */ + +#ifndef __bootloaderconfig_h_included__ +#define __bootloaderconfig_h_included__ + +/* +General Description: +This file (together with some settings in Makefile) configures the boot loader +according to the hardware. + +This file contains (besides the hardware configuration normally found in +usbconfig.h) two functions or macros: bootLoaderInit() and +bootLoaderCondition(). Whether you implement them as macros or as static +inline functions is up to you, decide based on code size and convenience. + +bootLoaderInit() is called as one of the first actions after reset. It should +be a minimum initialization of the hardware so that the boot loader condition +can be read. This will usually consist of activating a pull-up resistor for an +external jumper which selects boot loader mode. + +bootLoaderCondition() is called immediately after initialization and in each +main loop iteration. If it returns TRUE, the boot loader will be active. If it +returns FALSE, the boot loader jumps to address 0 (the loaded application) +immediately. + +For compatibility with Thomas Fischl's avrusbboot, we also support the macro +names BOOTLOADER_INIT and BOOTLOADER_CONDITION for this functionality. If +these macros are defined, the boot loader usees them. +*/ + +/* ---------------------------- Hardware Config ---------------------------- */ + +#define USB_CFG_IOPORTNAME D +/* This is the port where the USB bus is connected. When you configure it to + * "B", the registers PORTB, PINB and DDRB will be used. + */ +#define USB_CFG_DMINUS_BIT 7 // changed for USnooBie +/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. + * This may be any bit in the port. + */ +#define USB_CFG_DPLUS_BIT 2 +/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. + * This may be any bit in the port. Please note that D+ must also be connected + * to interrupt pin INT0! + */ +#define USB_CFG_CLOCK_KHZ (F_CPU/1000) +/* Clock rate of the AVR in MHz. Legal values are 12000, 16000 or 16500. + * The 16.5 MHz version of the code requires no crystal, it tolerates +/- 1% + * deviation from the nominal frequency. All other rates require a precision + * of 2000 ppm and thus a crystal! + * Default if not specified: 12 MHz + */ + +/* ----------------------- Optional Hardware Config ------------------------ */ + +/* #define USB_CFG_PULLUP_IOPORTNAME D */ +/* If you connect the 1.5k pullup resistor from D- to a port pin instead of + * V+, you can connect and disconnect the device from firmware by calling + * the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). + * This constant defines the port on which the pullup resistor is connected. + */ +/* #define USB_CFG_PULLUP_BIT 4 */ +/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined + * above) where the 1.5k pullup resistor is connected. See description + * above for details. + */ + +/* ------------------------------------------------------------------------- */ +/* ---------------------- feature / code size options ---------------------- */ +/* ------------------------------------------------------------------------- */ + +#define HAVE_EEPROM_PAGED_ACCESS 1 +/* If HAVE_EEPROM_PAGED_ACCESS is defined to 1, page mode access to EEPROM is + * compiled in. Whether page mode or byte mode access is used by AVRDUDE + * depends on the target device. Page mode is only used if the device supports + * it, e.g. for the ATMega88, 168 etc. You can save quite a bit of memory by + * disabling page mode EEPROM access. Costs ~ 138 bytes. + */ +#define HAVE_EEPROM_BYTE_ACCESS 1 +/* If HAVE_EEPROM_BYTE_ACCESS is defined to 1, byte mode access to EEPROM is + * compiled in. Byte mode is only used if the device (as identified by its + * signature) does not support page mode for EEPROM. It is required for + * accessing the EEPROM on the ATMega8. Costs ~54 bytes. + */ +#define BOOTLOADER_CAN_EXIT 1 +/* If this macro is defined to 1, the boot loader will exit shortly after the + * programmer closes the connection to the device. Costs ~36 bytes. + */ +#define HAVE_CHIP_ERASE 0 +/* If this macro is defined to 1, the boot loader implements the Chip Erase + * ISP command. Otherwise pages are erased on demand before they are written. + */ +//#define SIGNATURE_BYTES 0x1e, 0x93, 0x07, 0 /* ATMega8 */ +/* This macro defines the signature bytes returned by the emulated USBasp to + * the programmer software. They should match the actual device at least in + * memory size and features. If you don't define this, values for ATMega8, + * ATMega88, ATMega168 and ATMega328 are guessed correctly. + */ + +/* The following block guesses feature options so that the resulting code + * should fit into 2k bytes boot block with the given device and clock rate. + * Activate by passing "-DUSE_AUTOCONFIG=1" to the compiler. + * This requires gcc 3.4.6 for small enough code size! + */ +#if USE_AUTOCONFIG +# undef HAVE_EEPROM_PAGED_ACCESS +# define HAVE_EEPROM_PAGED_ACCESS (USB_CFG_CLOCK_KHZ >= 16000) +# undef HAVE_EEPROM_BYTE_ACCESS +# define HAVE_EEPROM_BYTE_ACCESS 1 +# undef BOOTLOADER_CAN_EXIT +# define BOOTLOADER_CAN_EXIT 1 +# undef SIGNATURE_BYTES +#endif /* USE_AUTOCONFIG */ + +/* ------------------------------------------------------------------------- */ + +/* Example configuration: Port D bit 3 is connected to a jumper which ties + * this pin to GND if the boot loader is requested. Initialization allows + * several clock cycles for the input voltage to stabilize before + * bootLoaderCondition() samples the value. + * We use a function for bootLoaderInit() for convenience and a macro for + * bootLoaderCondition() for efficiency. + */ + +#ifndef __ASSEMBLER__ /* assembler cannot parse function definitions */ + +#ifndef MCUCSR /* compatibility between ATMega8 and ATMega88 */ +# define MCUCSR MCUSR +#endif + +static inline void bootLoaderInit(void) +{ + PORTD |= (1 << 7); /* activate pull-up */ // changed to use D- pin for USnooBie + PORTD |= (1 << 4); /* button D4 */ + //if(!(MCUCSR & (1 << EXTRF))) /* If this was not an external reset, ignore */ + if(!(MCUCSR & (1 << PORF)) && !(MCUCSR & (1 << EXTRF))) /* If this was not a power-on reset or external reset, ignore */ + { + MCUCSR = 0; + leaveBootloader(); + } + MCUCSR = 0; /* clear all reset flags for next time */ +} + +static inline void bootLoaderExit(void) +{ + PORTD = 0; /* undo bootLoaderInit() changes */ +} + +#define bootLoaderCondition() ((PIND & (1 << 4)) == 0) // changed to use D- pin for USnooBie + +#endif /* __ASSEMBLER__ */ + +/* ------------------------------------------------------------------------- */ + +#endif /* __bootloader_h_included__ */ diff --git a/bootloader/burn.bat b/bootloader/burn.bat new file mode 100644 index 0000000..c681f11 --- /dev/null +++ b/bootloader/burn.bat @@ -0,0 +1,2 @@ +avrdude -c usbtiny -p m328p -F -U lfuse:w:0xFF:m -U hfuse:w:0xD8:m -U efuse:w:0x05:m -U lock:w:0x0F:m +avrdude -c usbtiny -p m328p -F -B 1 -U flash:w:main.hex diff --git a/bootloader/main.bin b/bootloader/main.bin new file mode 100755 index 0000000..95ae2b8 Binary files /dev/null and b/bootloader/main.bin differ diff --git a/bootloader/main.c b/bootloader/main.c new file mode 100644 index 0000000..2dd8b8c --- /dev/null +++ b/bootloader/main.c @@ -0,0 +1,316 @@ +/* Name: main.c + * Project: USBaspLoader + * Author: Christian Starkjohann + * Creation Date: 2007-12-08 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) + * This Revision: $Id: main.c 730 2009-03-20 09:05:11Z cs $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void leaveBootloader() __attribute__((__noreturn__)); + +#include "usbconfig.h" +#include "usbdrv/usbdrv.c" + +/* ------------------------------------------------------------------------ */ + +/* Request constants used by USBasp */ +#define USBASP_FUNC_CONNECT 1 +#define USBASP_FUNC_DISCONNECT 2 +#define USBASP_FUNC_TRANSMIT 3 +#define USBASP_FUNC_READFLASH 4 +#define USBASP_FUNC_ENABLEPROG 5 +#define USBASP_FUNC_WRITEFLASH 6 +#define USBASP_FUNC_READEEPROM 7 +#define USBASP_FUNC_WRITEEEPROM 8 +#define USBASP_FUNC_SETLONGADDRESS 9 + +/* ------------------------------------------------------------------------ */ + +#ifndef ulong +# define ulong unsigned long +#endif +#ifndef uint +# define uint unsigned int +#endif + +/* defaults if not in config file: */ +#ifndef HAVE_EEPROM_PAGED_ACCESS +# define HAVE_EEPROM_PAGED_ACCESS 0 +#endif +#ifndef HAVE_EEPROM_BYTE_ACCESS +# define HAVE_EEPROM_BYTE_ACCESS 0 +#endif +#ifndef BOOTLOADER_CAN_EXIT +# define BOOTLOADER_CAN_EXIT 0 +#endif + +/* allow compatibility with avrusbboot's bootloaderconfig.h: */ +#ifdef BOOTLOADER_INIT +# define bootLoaderInit() BOOTLOADER_INIT +# define bootLoaderExit() +#endif +#ifdef BOOTLOADER_CONDITION +# define bootLoaderCondition() BOOTLOADER_CONDITION +#endif + +/* device compatibility: */ +#ifndef GICR /* ATMega*8 don't have GICR, use MCUCR instead */ +# define GICR MCUCR +#endif + +/* ------------------------------------------------------------------------ */ + +#if (FLASHEND) > 0xffff /* we need long addressing */ +# define CURRENT_ADDRESS currentAddress.l +# define addr_t ulong +#else +# define CURRENT_ADDRESS currentAddress.w[0] +# define addr_t uint +#endif + +typedef union longConverter{ + addr_t l; + uint w[sizeof(addr_t)/2]; + uchar b[sizeof(addr_t)]; +}longConverter_t; + +static uchar requestBootLoaderExit = 0; +static longConverter_t currentAddress; /* in bytes */ +static uchar bytesRemaining; +static uchar isLastPage; +#if HAVE_EEPROM_PAGED_ACCESS +static uchar currentRequest; +#else +static const uchar currentRequest = 0; +#endif + +static const uchar signatureBytes[4] = { +#ifdef SIGNATURE_BYTES + SIGNATURE_BYTES +#elif defined (__AVR_ATmega8__) || defined (__AVR_ATmega8HVA__) + 0x1e, 0x93, 0x07, 0 +#elif defined (__AVR_ATmega48__) || defined (__AVR_ATmega48P__) + 0x1e, 0x92, 0x05, 0 +#elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega88P__) + 0x1e, 0x93, 0x0a, 0 +#elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega168P__) + 0x1e, 0x94, 0x06, 0 +#elif defined (__AVR_ATmega328P__) + 0x1e, 0x95, 0x0f, 0 +#else +# error "Device signature is not known, please edit main.c!" +#endif +}; + +/* ------------------------------------------------------------------------ */ + +static void (*nullVector)(void) __attribute__((__noreturn__)); + +static void leaveBootloader() +{ + //DBG1(0x01, 0, 0); + bootLoaderExit(); + cli(); + USB_INTR_ENABLE = 0; + USB_INTR_CFG = 0; /* also reset config bits */ + GICR = (1 << IVCE); /* enable change of interrupt vectors */ + GICR = (0 << IVSEL); /* move interrupts to application flash section */ +/* We must go through a global function pointer variable instead of writing + * ((void (*)(void))0)(); + * because the compiler optimizes a constant 0 to "rcall 0" which is not + * handled correctly by the assembler. + */ + nullVector(); +} + +/* ------------------------------------------------------------------------ */ + +uchar usbFunctionSetup(uchar data[8]) +{ +usbRequest_t *rq = (void *)data; +uchar len = 0; +static uchar replyBuffer[4]; + + usbMsgPtr = replyBuffer; + if(rq->bRequest == USBASP_FUNC_TRANSMIT){ /* emulate parts of ISP protocol */ + uchar rval = 0; + usbWord_t address; + address.bytes[1] = rq->wValue.bytes[1]; + address.bytes[0] = rq->wIndex.bytes[0]; + if(rq->wValue.bytes[0] == 0x30){ /* read signature */ + rval = rq->wIndex.bytes[0] & 3; + rval = signatureBytes[rval]; +#if HAVE_EEPROM_BYTE_ACCESS + }else if(rq->wValue.bytes[0] == 0xa0){ /* read EEPROM byte */ + rval = eeprom_read_byte((void *)address.word); + }else if(rq->wValue.bytes[0] == 0xc0){ /* write EEPROM byte */ + eeprom_write_byte((void *)address.word, rq->wIndex.bytes[1]); +#endif +#if HAVE_CHIP_ERASE + }else if(rq->wValue.bytes[0] == 0xac && rq->wValue.bytes[1] == 0x80){ /* chip erase */ + addr_t addr; + for(addr = 0; addr < FLASHEND + 1 - 2048; addr += SPM_PAGESIZE) { + /* wait and erase page */ + //DBG1(0x33, 0, 0); +# ifndef NO_FLASH_WRITE + boot_spm_busy_wait(); + cli(); + boot_page_erase(addr); + sei(); +# endif + } +#endif + }else{ + /* ignore all others, return default value == 0 */ + } + replyBuffer[3] = rval; + len = 4; + }else if(rq->bRequest == USBASP_FUNC_ENABLEPROG){ + /* replyBuffer[0] = 0; is never touched and thus always 0 which means success */ + len = 1; + }else if(rq->bRequest >= USBASP_FUNC_READFLASH && rq->bRequest <= USBASP_FUNC_SETLONGADDRESS){ + currentAddress.w[0] = rq->wValue.word; + if(rq->bRequest == USBASP_FUNC_SETLONGADDRESS){ +#if (FLASHEND) > 0xffff + currentAddress.w[1] = rq->wIndex.word; +#endif + }else{ + bytesRemaining = rq->wLength.bytes[0]; + /* if(rq->bRequest == USBASP_FUNC_WRITEFLASH) only evaluated during writeFlash anyway */ + isLastPage = rq->wIndex.bytes[1] & 0x02; +#if HAVE_EEPROM_PAGED_ACCESS + currentRequest = rq->bRequest; +#endif + len = 0xff; /* hand over to usbFunctionRead() / usbFunctionWrite() */ + } +#if BOOTLOADER_CAN_EXIT + }else if(rq->bRequest == USBASP_FUNC_DISCONNECT){ + requestBootLoaderExit = 1; /* allow proper shutdown/close of connection */ +#endif + }else{ + /* ignore: USBASP_FUNC_CONNECT */ + } + return len; +} + +uchar usbFunctionWrite(uchar *data, uchar len) +{ +uchar isLast; + + //DBG1(0x31, (void *)¤tAddress.l, 4); + if(len > bytesRemaining) + len = bytesRemaining; + bytesRemaining -= len; + isLast = bytesRemaining == 0; + if(currentRequest >= USBASP_FUNC_READEEPROM){ + uchar i; + for(i = 0; i < len; i++){ + eeprom_write_byte((void *)(currentAddress.w[0]++), *data++); + } + }else{ + uchar i; + for(i = 0; i < len;){ +#if !HAVE_CHIP_ERASE + if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0){ /* if page start: erase */ + //DBG1(0x33, 0, 0); +# ifndef NO_FLASH_WRITE + cli(); + boot_page_erase(CURRENT_ADDRESS); /* erase page */ + sei(); + boot_spm_busy_wait(); /* wait until page is erased */ +# endif + } +#endif + i += 2; + //DBG1(0x32, 0, 0); + cli(); + boot_page_fill(CURRENT_ADDRESS, *(short *)data); + sei(); + CURRENT_ADDRESS += 2; + data += 2; + /* write page when we cross page boundary or we have the last partial page */ + if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0 || (isLast && i >= len && isLastPage)){ + //DBG1(0x34, 0, 0); +#ifndef NO_FLASH_WRITE + cli(); + boot_page_write(CURRENT_ADDRESS - 2); + sei(); + boot_spm_busy_wait(); + cli(); + boot_rww_enable(); + sei(); +#endif + } + } + //DBG1(0x35, (void *)¤tAddress.l, 4); + } + return isLast; +} + +uchar usbFunctionRead(uchar *data, uchar len) +{ +uchar i; + + if(len > bytesRemaining) + len = bytesRemaining; + bytesRemaining -= len; + for(i = 0; i < len; i++){ + if(currentRequest >= USBASP_FUNC_READEEPROM){ + *data = eeprom_read_byte((void *)currentAddress.w[0]); + }else{ + *data = pgm_read_byte((void *)CURRENT_ADDRESS); + } + data++; + CURRENT_ADDRESS++; + } + return len; +} + +/* ------------------------------------------------------------------------ */ + +static void initForUsbConnectivity(void) +{ + uchar i = 0; + + usbInit(); + while (bootLoaderCondition()); + usbDeviceConnect(); + sei(); +} + +int main(void) +{ + /* initialize */ + wdt_disable(); /* main app may have enabled watchdog */ + bootLoaderInit(); + +#ifndef NO_FLASH_WRITE + GICR = (1 << IVCE); /* enable change of interrupt vectors */ + GICR = (1 << IVSEL); /* move interrupts to boot flash section */ +#endif + if (bootLoaderCondition()) { + //uchar i = 0, j = 0; + initForUsbConnectivity(); + for (;;) { // main loop + usbPoll(); +#if BOOTLOADER_CAN_EXIT + if (requestBootLoaderExit) break; +#endif + } + } + leaveBootloader(); + return 0; +} + +/* ------------------------------------------------------------------------ */ diff --git a/bootloader/main.hex b/bootloader/main.hex new file mode 100644 index 0000000..060e482 --- /dev/null +++ b/bootloader/main.hex @@ -0,0 +1,132 @@ +:107000000C945D380C9495380C947A380C947A383A +:107010000C947A380C947A380C947A380C947A3828 +:107020000C947A380C947A380C947A380C947A3818 +:107030000C947A380C947A380C947A380C947A3808 +:107040000C947A380C947A380C947A380C947A38F8 +:107050000C947A380C947A380C947A380C947A38E8 +:107060000C947A380C947A380902120001010080DD +:10707000FA09040000000000000012011001FF00E6 +:107080000008C016DC050201010200010E035500D4 +:10709000530042006100730070001C03770077000A +:1070A00077002E00660069007300630068006C00C2 +:1070B0002E00640065000403090411241FBECFEFF5 +:1070C000D8E0DEBFCDBF11E0A0E0B1E0E0E1F8E73D +:1070D00002C005900D92A630B107D9F711E0A6E0E5 +:1070E000B1E001C01D92AD33B107E1F70E94CF3985 +:1070F0000C94063C0C940038A82FB92F80E090E047 +:1071000041E050EA609530E009C02D9182279795C3 +:10711000879510F084279527305EC8F36F5FA8F33A +:107120000895EADF8D939D930895CF93CFB7CF93C2 +:10713000C3954F9BE9F74F9B09C04F9B07C04F9BDF +:1071400005C04F9B03C04F9B01C0A1C0DF93C091FE +:107150002001DD27C95DDE4F4F9B02C0DF91EBCFE1 +:107160002F930F931F9309B12FEF07FB20F94F9334 +:107170003F9319B14FEF012707FB21F93BE031C0E5 +:107180004E7F012F19B1216028C0102F4D7F226042 +:10719000000009B129C04B7F2460012F000019B104 +:1071A0002BC019B1477F28602AC04F7E09B12061EA +:1071B0002CC04F7D19B120622FC04F7B09B12064D4 +:1071C00032C0422709B149934FEF0000102717FB47 +:1071D00020F919B11478C9F1297F91F2012707FB31 +:1071E00021F909B1237F89F2315058F1102717FB9B +:1071F00022F919B1277E79F2012707FB23F92F7CA9 +:1072000081F209B1102717FB24F92F7971F200C020 +:1072100019B1012707FB25F92F7359F200C009B1F5 +:10722000102717FB26F9223040F200C019B10127C0 +:1072300007FB27F9243028F64F77206819B10000A2 +:10724000F9CF11E01CBB002717C03B503195C31B81 +:10725000D04011E01CBB0881033CE9F00B34D9F0AD +:1072600020911E011981110F1213EDCF093641F142 +:107270000D3211F0013E39F7009325013F914F91F6 +:107280001F910F912F91DF91CCB3C0FD51CFCF91C2 +:10729000CFBFCF91189520912501222379F310912A +:1072A0002301112311F5343012F13093230120937F +:1072B0001F01109120013BE0311B3093200117C0CA +:1072C0000091230101308CF40AE53091000134FD76 +:1072D00010C000930001C3E1D1E00FC02795A8F4CE +:1072E0005150A9F4220F0000F9CF4AE503C042ED46 +:1072F00001C0432FC4E1D0E032E01AB114685F9AB4 +:107300001AB90BB120E414E85F93012756E00BB9DA +:10731000279520F4515021F4220FF9CF012756E090 +:1073200000003B5A0BB9D0F2279528F4515029F4AC +:10733000220F0000F9CF012756E027950BB920F462 +:10734000515021F4220FF9CF012756E02991332320 +:107350000BB921F60B7710912401110FC651D040C3 +:107360000BB911F010931E0111E01CBB00681AB19B +:107370001B77402F4B775F9100C000C00BB91AB943 +:107380004BB97CCF1BB8F8941DBA1092690081E00C +:1073900085BF15BEE0911101F0911201099588E1B8 +:1073A0000FB6F89480936000109260000FBE5F9A51 +:1073B0005C9A04B600FC05C004B601FC02C014BE11 +:1073C00006C014BE81E085BF82E085BF4C990E9453 +:1073D000C23980916900836080936900E89A4C9B70 +:1073E000FECF579878942CE0E22EF12CCC24C39455 +:1073F00033E0B32E45E0A42E51E1952E88248394EA +:1074000063EC762E84E191E0D82E809123018350A5 +:1074100087FD57C190912001E701C91BD109C95DC2 +:10742000DE4F90911F019D3209F0C5C0883009F0F0 +:1074300046C1709213018AE5809300011092070102 +:10744000888180762981882309F445C088E091E00D +:1074500090932201809321012330D9F49B818C8168 +:107460002A81203339F48370E82FF0E0EE5FFE4F7D +:1074700080810BC0203A19F40E94F03B06C0203CEA +:1074800019F46D810E94F83B80E080930B0124E0A9 +:1074900088C02530F1F0822F84508630A0F48A8194 +:1074A0009B8190930D0180930C01293009F40FC04A +:1074B0008E8180930E018D81827080930F012093C5 +:1074C00010012FEF67C0223011F48092060120E0F6 +:1074D00068C021E066C08A8110921C01211106C09B +:1074E00010921D018CE191E022E04EC0253019F48C +:1074F000809324013DC02630A9F59B81913019F479 +:107500008AE790E704C0923041F488E690E79093D0 +:1075100022018093210122E121C09330F1F48111F5 +:1075200008C086EB90E7909322018093210124E02C +:1075300015C0813041F48AE990E79093220180934D +:1075400021012CE10BC0823041F48CE890E790934C +:107550002201809321012EE001C020E080E480938D +:10756000070116C0283069F0293029F480932601DC +:107570008CE191E004C08CE191E02A3021F020E020 +:1075800003C086E291E021E09093220180932101E3 +:107590002F3F39F4888187FD2E8180E88093070191 +:1075A00007C08F81811104C08E81821708F4282FB3 +:1075B0002093010184C09091070197FF80C09091B2 +:1075C0000E01682E981708F4692E961990930E01F3 +:1075D000552453949111512C80911001873010F44F +:1075E0002C2F65C08E0110C0F80161918F01809130 +:1075F0000C0190910D019C012F5F3F4F30930D01C5 +:1076000020930C010E94F83B802F8C1B861560F3A1 +:10761000511053C054C080910C0190910D018F778F +:107620009927892BD9F0F894E0910C01F0910D0184 +:10763000899199910C01C0925700E8951124789492 +:1076400080910C0190910D01029690930D01809311 +:107650000C018F779927892BC1F00DC0F894E09128 +:107660000C01F0910D01B0925700E895789407B69F +:1076700000FCFDCFD8CF5520D1F08C2F821B861572 +:10768000B0F080910F01882391F0F894E0910C0103 +:10769000F0910D013297A0925700E895789407B6C3 +:1076A00000FCFDCFF89490925700E89578948C2FC9 +:1076B000821B861508F4AFCFABCF10920101109258 +:1076C00023018091000184FF7BC0809101018F3FE5 +:1076D00009F476C0682E893010F098E0692E86197A +:1076E000809301018091130198E889278093130109 +:1076F000662009F452C08091070187FF27C080915E +:107700000E01861508F4682E861980930E0150909C +:107710001001C4E1D1E015C000910C0110910D01E0 +:1077200086E0851520F4C8010E94F03B02C0F801F4 +:107730008491888321960F5F1F4F10930D01009352 +:107740000C018C2F8D19861538F324C0209121014E +:107750003091220186FF0BC0F901A4E1B1E0862D32 +:107760008D0D94919D9331968A13FBCF09C0D90159 +:10777000E4E1F1E0862D8D0D9D9191938E13FCCF68 +:10778000862D815090E00196820F931F90932201E5 +:1077900080932101F8E0F61568F0662D84E191E010 +:1077A0000E949138862D8C5F8C3041F09FEF909332 +:1077B000010104C08FEF809301018EE180930001ED +:1077C00084E199B1947831F48150D9F71092240171 +:1077D00010921E0180910601882309F416CEF7CD80 +:1077E000F999FECF92BD81BDF89A992780B5089589 +:1077F000262FF999FECF1FBA92BD81BD20BD0FB6CD +:10780000F894FA9AF99A0FBE01960895F894FFCF6A +:067810005AFF1E950F0057 +:040000030000700089 +:00000001FF diff --git a/bootloader/main.o b/bootloader/main.o new file mode 100644 index 0000000..41c1973 Binary files /dev/null and b/bootloader/main.o differ diff --git a/bootloader/readme.txt b/bootloader/readme.txt new file mode 100644 index 0000000..2201e6b --- /dev/null +++ b/bootloader/readme.txt @@ -0,0 +1,16 @@ +========================================================================== + +NOTE: the original code by Frank Zhao has been slightly modified to use +button on pin PD4 for bootloading condition. + +========================================================================== + +bootloader files for USnooBie, base off USBaspLoader + +all hex files generated August 16 2010 by Frank Zhao for USnooBie bootloader, WinAVR20060421 is used to for the non-328 variants, and WinAVR20100110 is used for the 328P variant, V-USB 20100715 is used for usbdrv + +due to the size limits of the bootloader, the ATmega8/48/88/168 variants requires WinAVR 20060421 while ATmega328P requires any WinAVR version that supports ATmega328P + +fuses.txt tells you the recommended fuse bits to use with each chip + +If the size of the bootloader exceeds 2048 bytes, then you must use the 4k bootloader file and fuses, if it is under or exactly 2048 bytes, then you may use the 2k bootloader files and fuses. Only ATmega328P is capable of using bootloaders exceeding 2048 bytes. diff --git a/bootloader/usbconfig.h b/bootloader/usbconfig.h new file mode 100644 index 0000000..bf1fa3f --- /dev/null +++ b/bootloader/usbconfig.h @@ -0,0 +1,214 @@ +/* Name: usbconfig.h + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2007-12-08 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt) + * This Revision: $Id: usbconfig.h 449 2007-12-13 22:25:49Z cs $ + */ + +#ifndef __usbconfig_h_included__ +#define __usbconfig_h_included__ + +/* +General Description: +This file contains the configuration options for the USB driver. + +Please note that the usbdrv contains a usbconfig-prototype.h file now. We +recommend that you use that file as a template because it will always list +the newest features and options. +*/ + +/* Fetch the hardware configuration from bootloaderconfig.h so that we have a + * single file where hardware settings are stored. + * Do not edit the functional settings below. + */ +#include "bootloaderconfig.h" + +#define USB_PUBLIC static +/* Use the define above if you #include usbdrv.c instead of linking against it. + * This technique saves a couple of bytes in flash memory. + */ + +/* --------------------------- Functional Range ---------------------------- */ + +#define USB_CFG_HAVE_INTRIN_ENDPOINT 0 +/* Define this to 1 if you want to compile a version with two endpoints: The + * default control endpoint 0 and an interrupt-in endpoint 1. + */ +#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 +/* Define this to 1 if you want to compile a version with three endpoints: The + * default control endpoint 0, an interrupt-in endpoint 1 and an interrupt-in + * endpoint 3. You must also enable endpoint 1 above. + */ +#define USB_CFG_IMPLEMENT_HALT 0 +/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature + * for endpoint 1 (interrupt endpoint). Although you may not need this feature, + * it is required by the standard. We have made it a config option because it + * bloats the code considerably. + */ +#define USB_CFG_INTR_POLL_INTERVAL 200 +/* If you compile a version with endpoint 1 (interrupt-in), this is the poll + * interval. The value is in milliseconds and must not be less than 10 ms for + * low speed devices. + */ +#define USB_CFG_IS_SELF_POWERED 0 +/* Define this to 1 if the device has its own power supply. Set it to 0 if the + * device is powered from the USB bus. + */ +#define USB_CFG_MAX_BUS_POWER 500 +/* Set this variable to the maximum USB bus power consumption of your device. + * The value is in milliamperes. [It will be divided by two since USB + * communicates power requirements in units of 2 mA.] + */ +#define USB_CFG_IMPLEMENT_FN_WRITE 1 +/* Set this to 1 if you want usbFunctionWrite() to be called for control-out + * transfers. Set it to 0 if you don't need it and want to save a couple of + * bytes. + */ +#define USB_CFG_IMPLEMENT_FN_READ 1 +/* Set this to 1 if you need to send control replies which are generated + * "on the fly" when usbFunctionRead() is called. If you only want to send + * data from a static buffer, set it to 0 and return the data from + * usbFunctionSetup(). This saves a couple of bytes. + */ +#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0 +/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoint 1. + * You must implement the function usbFunctionWriteOut() which receives all + * interrupt/bulk data sent to endpoint 1. + */ +#define USB_CFG_HAVE_FLOWCONTROL 0 +/* Define this to 1 if you want flowcontrol over USB data. See the definition + * of the macros usbDisableAllRequests() and usbEnableAllRequests() in + * usbdrv.h. + */ + +/* -------------------------- Device Description --------------------------- */ + +#define USB_CFG_VENDOR_ID 0xc0, 0x16 /* 5824 in dec, stands for VOTI */ +/* USB vendor ID for the device, low byte first. If you have registered your + * own Vendor ID, define it here. Otherwise you use obdev's free shared + * VID/PID pair. Be sure to read USBID-License.txt for rules! + */ +#define USB_CFG_DEVICE_ID 0xdc, 0x05 /* 1500 in dec, obdev's free PID */ +/* This is the ID of the product, low byte first. It is interpreted in the + * scope of the vendor ID. If you have registered your own VID with usb.org + * or if you have licensed a PID from somebody else, define it here. Otherwise + * you use obdev's free shared VID/PID pair. Be sure to read the rules in + * USBID-License.txt! + */ +#define USB_CFG_DEVICE_VERSION 0x02, 0x01 +/* Version number of the device: Minor number first, then major number. + */ +#define USB_CFG_VENDOR_NAME 'w', 'w', 'w', '.', 'f', 'i', 's', 'c', 'h', 'l', '.', 'd', 'e' +#define USB_CFG_VENDOR_NAME_LEN 13 +/* These two values define the vendor name returned by the USB device. The name + * must be given as a list of characters under single quotes. The characters + * are interpreted as Unicode (UTF-16) entities. + * If you don't want a vendor name string, undefine these macros. + * ALWAYS define a vendor name containing your Internet domain name if you use + * obdev's free shared VID/PID pair. See the file USBID-License.txt for + * details. + */ +#define USB_CFG_DEVICE_NAME 'U', 'S', 'B', 'a', 's', 'p' +#define USB_CFG_DEVICE_NAME_LEN 6 +/* Same as above for the device name. If you don't want a device name, undefine + * the macros. See the file USBID-License.txt before you assign a name. + */ +/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */ +/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */ +/* Same as above for the serial number. If you don't want a serial number, + * undefine the macros. + * It may be useful to provide the serial number through other means than at + * compile time. See the section about descriptor properties below for how + * to fine tune control over USB descriptors such as the string descriptor + * for the serial number. + */ +#define USB_CFG_DEVICE_CLASS 0xff +#define USB_CFG_DEVICE_SUBCLASS 0 +/* See USB specification if you want to conform to an existing device class. + */ +#define USB_CFG_INTERFACE_CLASS 0 +#define USB_CFG_INTERFACE_SUBCLASS 0 +#define USB_CFG_INTERFACE_PROTOCOL 0 +/* See USB specification if you want to conform to an existing device class or + * protocol. + */ +#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 0 /* total length of report descriptor */ +/* Define this to the length of the HID report descriptor, if you implement + * an HID device. Otherwise don't define it or define it to 0. + */ + +/* ------------------- Fine Control over USB Descriptors ------------------- */ +/* If you don't want to use the driver's default USB descriptors, you can + * provide our own. These can be provided as (1) fixed length static data in + * flash memory, (2) fixed length static data in RAM or (3) dynamically at + * runtime in the function usbFunctionDescriptor(). See usbdrv.h for more + * information about this function. + * Descriptor handling is configured through the descriptor's properties. If + * no properties are defined or if they are 0, the default descriptor is used. + * Possible properties are: + * + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched + * at runtime via usbFunctionDescriptor(). + * + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found + * in static memory is in RAM, not in flash memory. + * + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), + * the driver must know the descriptor's length. The descriptor itself is + * found at the address of a well known identifier (see below). + * List of static descriptor names (must be declared PROGMEM if in flash): + * char usbDescriptorDevice[]; + * char usbDescriptorConfiguration[]; + * char usbDescriptorHidReport[]; + * char usbDescriptorString0[]; + * int usbDescriptorStringVendor[]; + * int usbDescriptorStringDevice[]; + * int usbDescriptorStringSerialNumber[]; + * Other descriptors can't be provided statically, they must be provided + * dynamically at runtime. + * + * Descriptor properties are or-ed or added together, e.g.: + * #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) + * + * The following descriptors are defined: + * USB_CFG_DESCR_PROPS_DEVICE + * USB_CFG_DESCR_PROPS_CONFIGURATION + * USB_CFG_DESCR_PROPS_STRINGS + * USB_CFG_DESCR_PROPS_STRING_0 + * USB_CFG_DESCR_PROPS_STRING_VENDOR + * USB_CFG_DESCR_PROPS_STRING_PRODUCT + * USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER + * USB_CFG_DESCR_PROPS_HID + * USB_CFG_DESCR_PROPS_HID_REPORT + * USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) + * + */ + +#define USB_CFG_DESCR_PROPS_DEVICE 0 +#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 +#define USB_CFG_DESCR_PROPS_STRINGS 0 +#define USB_CFG_DESCR_PROPS_STRING_0 0 +#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 +#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 +#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 +#define USB_CFG_DESCR_PROPS_HID 0 +#define USB_CFG_DESCR_PROPS_HID_REPORT 0 +#define USB_CFG_DESCR_PROPS_UNKNOWN 0 + +/* ----------------------- Optional MCU Description ------------------------ */ + +/* The following configurations have working defaults in usbdrv.h. You + * usually don't need to set them explicitly. Only if you want to run + * the driver on a device which is not yet supported or with a compiler + * which is not fully supported (such as IAR C) or if you use a differnt + * interrupt than INT0, you may have to define some of these. + */ +/* #define USB_INTR_CFG MCUCR */ +/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */ +/* #define USB_INTR_CFG_CLR 0 */ +/* #define USB_INTR_ENABLE GIMSK */ +/* #define USB_INTR_ENABLE_BIT INT0 */ +/* #define USB_INTR_PENDING GIFR */ +/* #define USB_INTR_PENDING_BIT INTF0 */ + +#endif /* __usbconfig_h_included__ */ diff --git a/bootloader/usbdrv/Changelog.txt b/bootloader/usbdrv/Changelog.txt new file mode 100644 index 0000000..5c6354a --- /dev/null +++ b/bootloader/usbdrv/Changelog.txt @@ -0,0 +1,308 @@ +This file documents changes in the firmware-only USB driver for atmel's AVR +microcontrollers. New entries are always appended to the end of the file. +Scroll down to the bottom to see the most recent changes. + +2005-04-01: + - Implemented endpoint 1 as interrupt-in endpoint. + - Moved all configuration options to usbconfig.h which is not part of the + driver. + - Changed interface for usbVendorSetup(). + - Fixed compatibility with ATMega8 device. + - Various minor optimizations. + +2005-04-11: + - Changed interface to application: Use usbFunctionSetup(), usbFunctionRead() + and usbFunctionWrite() now. Added configuration options to choose which + of these functions to compile in. + - Assembler module delivers receive data non-inverted now. + - Made register and bit names compatible with more AVR devices. + +2005-05-03: + - Allow address of usbRxBuf on any memory page as long as the buffer does + not cross 256 byte page boundaries. + - Better device compatibility: works with Mega88 now. + - Code optimization in debugging module. + - Documentation updates. + +2006-01-02: + - Added (free) default Vendor- and Product-IDs bought from voti.nl. + - Added USBID-License.txt file which defines the rules for using the free + shared VID/PID pair. + - Added Readme.txt to the usbdrv directory which clarifies administrative + issues. + +2006-01-25: + - Added "configured state" to become more standards compliant. + - Added "HALT" state for interrupt endpoint. + - Driver passes the "USB Command Verifier" test from usb.org now. + - Made "serial number" a configuration option. + - Minor optimizations, we now recommend compiler option "-Os" for best + results. + - Added a version number to usbdrv.h + +2006-02-03: + - New configuration variable USB_BUFFER_SECTION for the memory section where + the USB rx buffer will go. This defaults to ".bss" if not defined. Since + this buffer MUST NOT cross 256 byte pages (not even touch a page at the + end), the user may want to pass a linker option similar to + "-Wl,--section-start=.mybuffer=0x800060". + - Provide structure for usbRequest_t. + - New defines for USB constants. + - Prepared for HID implementations. + - Increased data size limit for interrupt transfers to 8 bytes. + - New macro usbInterruptIsReady() to query interrupt buffer state. + +2006-02-18: + - Ensure that the data token which is sent as an ack to an OUT transfer is + always zero sized. This fixes a bug where the host reports an error after + sending an out transfer to the device, although all data arrived at the + device. + - Updated docs in usbdrv.h to reflect changed API in usbFunctionWrite(). + +* Release 2006-02-20 + + - Give a compiler warning when compiling with debugging turned on. + - Added Oleg Semyonov's changes for IAR-cc compatibility. + - Added new (optional) functions usbDeviceConnect() and usbDeviceDisconnect() + (also thanks to Oleg!). + - Rearranged tests in usbPoll() to save a couple of instructions in the most + likely case that no actions are pending. + - We need a delay between the SET ADDRESS request until the new address + becomes active. This delay was handled in usbPoll() until now. Since the + spec says that the delay must not exceed 2ms, previous versions required + aggressive polling during the enumeration phase. We have now moved the + handling of the delay into the interrupt routine. + - We must not reply with NAK to a SETUP transaction. We can only achieve this + by making sure that the rx buffer is empty when SETUP tokens are expected. + We therefore don't pass zero sized data packets from the status phase of + a transfer to usbPoll(). This change MAY cause troubles if you rely on + receiving a less than 8 bytes long packet in usbFunctionWrite() to + identify the end of a transfer. usbFunctionWrite() will NEVER be called + with a zero length. + +* Release 2006-03-14 + + - Improved IAR C support: tiny memory model, more devices + - Added template usbconfig.h file under the name usbconfig-prototype.h + +* Release 2006-03-26 + + - Added provision for one more interrupt-in endpoint (endpoint 3). + - Added provision for one interrupt-out endpoint (endpoint 1). + - Added flowcontrol macros for USB. + - Added provision for custom configuration descriptor. + - Allow ANY two port bits for D+ and D-. + - Merged (optional) receive endpoint number into global usbRxToken variable. + - Use USB_CFG_IOPORTNAME instead of USB_CFG_IOPORT. We now construct the + variable name from the single port letter instead of computing the address + of related ports from the output-port address. + +* Release 2006-06-26 + + - Updated documentation in usbdrv.h and usbconfig-prototype.h to reflect the + new features. + - Removed "#warning" directives because IAR does not understand them. Use + unused static variables instead to generate a warning. + - Do not include when compiling with IAR. + - Introduced USB_CFG_DESCR_PROPS_* in usbconfig.h to configure how each + USB descriptor should be handled. It is now possible to provide descriptor + data in Flash, RAM or dynamically at runtime. + - STALL is now a status in usbTxLen* instead of a message. We can now conform + to the spec and leave the stall status pending until it is cleared. + - Made usbTxPacketCnt1 and usbTxPacketCnt3 public. This allows the + application code to reset data toggling on interrupt pipes. + +* Release 2006-07-18 + + - Added an #if !defined __ASSEMBLER__ to the warning in usbdrv.h. This fixes + an assembler error. + - usbDeviceDisconnect() takes pull-up resistor to high impedance now. + +* Release 2007-02-01 + + - Merged in some code size improvements from usbtiny (thanks to Dick + Streefland for these optimizations!) + - Special alignment requirement for usbRxBuf not required any more. Thanks + again to Dick Streefland for this hint! + - Reverted to "#warning" instead of unused static variables -- new versions + of IAR CC should handle this directive. + - Changed Open Source license to GNU GPL v2 in order to make linking against + other free libraries easier. We no longer require publication of the + circuit diagrams, but we STRONGLY encourage it. If you improve the driver + itself, PLEASE grant us a royalty free license to your changes for our + commercial license. + +* Release 2007-03-29 + + - New configuration option "USB_PUBLIC" in usbconfig.h. + - Set USB version number to 1.10 instead of 1.01. + - Code used USB_CFG_DESCR_PROPS_STRING_DEVICE and + USB_CFG_DESCR_PROPS_STRING_PRODUCT inconsistently. Changed all occurrences + to USB_CFG_DESCR_PROPS_STRING_PRODUCT. + - New assembler module for 16.5 MHz RC oscillator clock with PLL in receiver + code. + - New assembler module for 16 MHz crystal. + - usbdrvasm.S contains common code only, clock-specific parts have been moved + to usbdrvasm12.S, usbdrvasm16.S and usbdrvasm165.S respectively. + +* Release 2007-06-25 + + - 16 MHz module: Do SE0 check in stuffed bits as well. + +* Release 2007-07-07 + + - Define hi8(x) for IAR compiler to limit result to 8 bits. This is necessary + for negative values. + - Added 15 MHz module contributed by V. Bosch. + - Interrupt vector name can now be configured. This is useful if somebody + wants to use a different hardware interrupt than INT0. + +* Release 2007-08-07 + + - Moved handleIn3 routine in usbdrvasm16.S so that relative jump range is + not exceeded. + - More config options: USB_RX_USER_HOOK(), USB_INITIAL_DATATOKEN, + USB_COUNT_SOF + - USB_INTR_PENDING can now be a memory address, not just I/O + +* Release 2007-09-19 + + - Split out common parts of assembler modules into separate include file + - Made endpoint numbers configurable so that given interface definitions + can be matched. See USB_CFG_EP3_NUMBER in usbconfig-prototype.h. + - Store endpoint number for interrupt/bulk-out so that usbFunctionWriteOut() + can handle any number of endpoints. + - Define usbDeviceConnect() and usbDeviceDisconnect() even if no + USB_CFG_PULLUP_IOPORTNAME is defined. Directly set D+ and D- to 0 in this + case. + +* Release 2007-12-01 + + - Optimize usbDeviceConnect() and usbDeviceDisconnect() for less code size + when USB_CFG_PULLUP_IOPORTNAME is not defined. + +* Release 2007-12-13 + + - Renamed all include-only assembler modules from *.S to *.inc so that + people don't add them to their project sources. + - Distribute leap bits in tx loop more evenly for 16 MHz module. + - Use "macro" and "endm" instead of ".macro" and ".endm" for IAR + - Avoid compiler warnings for constant expr range by casting some values in + USB descriptors. + +* Release 2008-01-21 + + - Fixed bug in 15 and 16 MHz module where the new address set with + SET_ADDRESS was already accepted at the next NAK or ACK we send, not at + the next data packet we send. This caused problems when the host polled + too fast. Thanks to Alexander Neumann for his help and patience debugging + this issue! + +* Release 2008-02-05 + + - Fixed bug in 16.5 MHz module where a register was used in the interrupt + handler before it was pushed. This bug was introduced with version + 2007-09-19 when common parts were moved to a separate file. + - Optimized CRC routine (thanks to Reimar Doeffinger). + +* Release 2008-02-16 + + - Removed outdated IAR compatibility stuff (code sections). + - Added hook macros for USB_RESET_HOOK() and USB_SET_ADDRESS_HOOK(). + - Added optional routine usbMeasureFrameLength() for calibration of the + internal RC oscillator. + +* Release 2008-02-28 + + - USB_INITIAL_DATATOKEN defaults to USBPID_DATA1 now, which means that we + start with sending USBPID_DATA0. + - Changed defaults in usbconfig-prototype.h + - Added free USB VID/PID pair for MIDI class devices + - Restructured AVR-USB as separate package, not part of PowerSwitch any more. + +* Release 2008-04-18 + + - Restructured usbdrv.c so that it is easier to read and understand. + - Better code optimization with gcc 4. + - If a second interrupt in endpoint is enabled, also add it to config + descriptor. + - Added config option for long transfers (above 254 bytes), see + USB_CFG_LONG_TRANSFERS in usbconfig.h. + - Added 20 MHz module contributed by Jeroen Benschop. + +* Release 2008-05-13 + + - Fixed bug in libs-host/hiddata.c function usbhidGetReport(): length + was not incremented, pointer to length was incremented instead. + - Added code to command line tool(s) which claims an interface. This code + is disabled by default, but may be necessary on newer Linux kernels. + - Added usbconfig.h option "USB_CFG_CHECK_DATA_TOGGLING". + - New header "usbportability.h" prepares ports to other development + environments. + - Long transfers (above 254 bytes) did not work when usbFunctionRead() was + used to supply the data. Fixed this bug. [Thanks to Alexander Neumann!] + - In hiddata.c (example code for sending/receiving data over HID), use + USB_RECIP_DEVICE instead of USB_RECIP_INTERFACE for control transfers so + that we need not claim the interface. + - in usbPoll() loop 20 times polling for RESET state instead of 10 times. + This accounts for the higher clock rates we now support. + - Added a module for 12.8 MHz RC oscillator with PLL in receiver loop. + - Added hook to SOF code so that oscillator can be tuned to USB frame clock. + - Added timeout to waitForJ loop. Helps preventing unexpected hangs. + - Added example code for oscillator tuning to libs-device (thanks to + Henrik Haftmann for the idea to this routine). + - Implemented option USB_CFG_SUPPRESS_INTR_CODE. + +* Release 2008-10-22 + + - Fixed libs-device/osctune.h: OSCCAL is memory address on ATMega88 and + similar, not offset of 0x20 needs to be added. + - Allow distribution under GPLv3 for those who have to link against other + code distributed under GPLv3. + +* Release 2008-11-26 + + - Removed libusb-win32 dependency for hid-data example in Makefile.windows. + It was never required and confused many people. + - Added extern uchar usbRxToken to usbdrv.h. + - Integrated a module with CRC checks at 18 MHz by Lukas Schrittwieser. + +* Release 2009-03-23 + + - Hid-mouse example used settings from hid-data example, fixed that. + - Renamed project to V-USB due to a trademark issue with Atmel(r). + - Changed CommercialLicense.txt and USBID-License.txt to make the + background of USB ID registration clearer. + +* Release 2009-04-15 + + - Changed CommercialLicense.txt to reflect the new range of PIDs from + Jason Kotzin. + - Removed USBID-License.txt in favor of USB-IDs-for-free.txt and + USB-ID-FAQ.txt + - Fixed a bug in the 12.8 MHz module: End Of Packet decection was made in + the center between bit 0 and 1 of each byte. This is where the data lines + are expected to change and the sampled data may therefore be nonsense. + We therefore check EOP ONLY if bits 0 AND 1 have both been read as 0 on D-. + - Fixed a bitstuffing problem in the 16 MHz module: If bit 6 was stuffed, + the unstuffing code in the receiver routine was 1 cycle too long. If + multiple bytes had the unstuffing in bit 6, the error summed up until the + receiver was out of sync. + - Included option for faster CRC routine. + Thanks to Slawomir Fras (BoskiDialer) for this code! + - Updated bits in Configuration Descriptor's bmAttributes according to + USB 1.1 (in particular bit 7, it is a must-be-set bit now). + +* Release 2009-08-22 + + - Moved first DBG1() after odDebugInit() in all examples. + - Use vector INT0_vect instead of SIG_INTERRUPT0 if defined. This makes + V-USB compatible with the new "p" suffix devices (e.g. ATMega328p). + - USB_CFG_CLOCK_KHZ setting is now required in usbconfig.h (no default any + more). + - New option USB_CFG_DRIVER_FLASH_PAGE allows boot loaders on devices with + more than 64 kB flash. + - Built-in configuration descriptor allows custom definition for second + endpoint now. + +* Release 2010-07-15 diff --git a/bootloader/usbdrv/CommercialLicense.txt b/bootloader/usbdrv/CommercialLicense.txt new file mode 100644 index 0000000..11d07d9 --- /dev/null +++ b/bootloader/usbdrv/CommercialLicense.txt @@ -0,0 +1,166 @@ +V-USB Driver Software License Agreement +Version 2009-08-03 + +THIS LICENSE AGREEMENT GRANTS YOU CERTAIN RIGHTS IN A SOFTWARE. YOU CAN +ENTER INTO THIS AGREEMENT AND ACQUIRE THE RIGHTS OUTLINED BELOW BY PAYING +THE AMOUNT ACCORDING TO SECTION 4 ("PAYMENT") TO OBJECTIVE DEVELOPMENT. + + +1 DEFINITIONS + +1.1 "OBJECTIVE DEVELOPMENT" shall mean OBJECTIVE DEVELOPMENT Software GmbH, +Grosse Schiffgasse 1A/7, 1020 Wien, AUSTRIA. + +1.2 "You" shall mean the Licensee. + +1.3 "V-USB" shall mean all files included in the package distributed under +the name "vusb" by OBJECTIVE DEVELOPMENT (http://www.obdev.at/vusb/) +unless otherwise noted. This includes the firmware-only USB device +implementation for Atmel AVR microcontrollers, some simple device examples +and host side software examples and libraries. + + +2 LICENSE GRANTS + +2.1 Source Code. OBJECTIVE DEVELOPMENT shall furnish you with the source +code of V-USB. + +2.2 Distribution and Use. OBJECTIVE DEVELOPMENT grants you the +non-exclusive right to use, copy and distribute V-USB with your hardware +product(s), restricted by the limitations in section 3 below. + +2.3 Modifications. OBJECTIVE DEVELOPMENT grants you the right to modify +the source code and your copy of V-USB according to your needs. + +2.4 USB IDs. OBJECTIVE DEVELOPMENT furnishes you with one or two USB +Product ID(s), sent to you in e-mail. These Product IDs are reserved +exclusively for you. OBJECTIVE DEVELOPMENT has obtained USB Product ID +ranges under the Vendor ID 5824 from Wouter van Ooijen (Van Ooijen +Technische Informatica, www.voti.nl) and under the Vendor ID 8352 from +Jason Kotzin (Clay Logic, www.claylogic.com). Both owners of the Vendor IDs +have obtained these IDs from the USB Implementers Forum, Inc. +(www.usb.org). OBJECTIVE DEVELOPMENT disclaims all liability which might +arise from the assignment of USB IDs. + +2.5 USB Certification. Although not part of this agreement, we want to make +it clear that you cannot become USB certified when you use V-USB or a USB +Product ID assigned by OBJECTIVE DEVELOPMENT. AVR microcontrollers don't +meet the electrical specifications required by the USB specification and +the USB Implementers Forum certifies only members who bought a Vendor ID of +their own. + + +3 LICENSE RESTRICTIONS + +3.1 Number of Units. Only one of the following three definitions is +applicable. Which one is determined by the amount you pay to OBJECTIVE +DEVELOPMENT, see section 4 ("Payment") below. + +Hobby License: You may use V-USB according to section 2 above in no more +than 5 hardware units. These units must not be sold for profit. + +Entry Level License: You may use V-USB according to section 2 above in no +more than 150 hardware units. + +Professional License: You may use V-USB according to section 2 above in +any number of hardware units, except for large scale production ("unlimited +fair use"). Quantities below 10,000 units are not considered large scale +production. If your reach quantities which are obviously large scale +production, you must pay a license fee of 0.10 EUR per unit for all units +above 10,000. + +3.2 Rental. You may not rent, lease, or lend V-USB or otherwise encumber +any copy of V-USB, or any of the rights granted herein. + +3.3 Transfer. You may not transfer your rights under this Agreement to +another party without OBJECTIVE DEVELOPMENT's prior written consent. If +such consent is obtained, you may permanently transfer this License to +another party. The recipient of such transfer must agree to all terms and +conditions of this Agreement. + +3.4 Reservation of Rights. OBJECTIVE DEVELOPMENT retains all rights not +expressly granted. + +3.5 Non-Exclusive Rights. Your license rights under this Agreement are +non-exclusive. + +3.6 Third Party Rights. This Agreement cannot grant you rights controlled +by third parties. In particular, you are not allowed to use the USB logo or +other trademarks owned by the USB Implementers Forum, Inc. without their +consent. Since such consent depends on USB certification, it should be +noted that V-USB will not pass certification because it does not +implement checksum verification and the microcontroller ports do not meet +the electrical specifications. + + +4 PAYMENT + +The payment amount depends on the variation of this agreement (according to +section 3.1) into which you want to enter. Concrete prices are listed on +OBJECTIVE DEVELOPMENT's web site, usually at +http://www.obdev.at/vusb/license.html. You agree to pay the amount listed +there to OBJECTIVE DEVELOPMENT or OBJECTIVE DEVELOPMENT's payment processor +or reseller. + + +5 COPYRIGHT AND OWNERSHIP + +V-USB is protected by copyright laws and international copyright +treaties, as well as other intellectual property laws and treaties. V-USB +is licensed, not sold. + + +6 TERM AND TERMINATION + +6.1 Term. This Agreement shall continue indefinitely. However, OBJECTIVE +DEVELOPMENT may terminate this Agreement and revoke the granted license and +USB-IDs if you fail to comply with any of its terms and conditions. + +6.2 Survival of Terms. All provisions regarding secrecy, confidentiality +and limitation of liability shall survive termination of this agreement. + + +7 DISCLAIMER OF WARRANTY AND LIABILITY + +LIMITED WARRANTY. V-USB IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, OBJECTIVE +DEVELOPMENT AND ITS SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND +NON-INFRINGEMENT, WITH REGARD TO V-USB, AND THE PROVISION OF OR FAILURE +TO PROVIDE SUPPORT SERVICES. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL +RIGHTS. YOU MAY HAVE OTHERS, WHICH VARY FROM STATE/JURISDICTION TO +STATE/JURISDICTION. + +LIMITATION OF LIABILITY. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, +IN NO EVENT SHALL OBJECTIVE DEVELOPMENT OR ITS SUPPLIERS BE LIABLE FOR ANY +SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER +(INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, +BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY +LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE V-USB OR THE +PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF OBJECTIVE +DEVELOPMENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY +CASE, OBJECTIVE DEVELOPMENT'S ENTIRE LIABILITY UNDER ANY PROVISION OF THIS +AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR V-USB. + + +8 MISCELLANEOUS TERMS + +8.1 Marketing. OBJECTIVE DEVELOPMENT has the right to mention for marketing +purposes that you entered into this agreement. + +8.2 Entire Agreement. This document represents the entire agreement between +OBJECTIVE DEVELOPMENT and you. It may only be modified in writing signed by +an authorized representative of both, OBJECTIVE DEVELOPMENT and you. + +8.3 Severability. In case a provision of these terms and conditions should +be or become partly or entirely invalid, ineffective, or not executable, +the validity of all other provisions shall not be affected. + +8.4 Applicable Law. This agreement is governed by the laws of the Republic +of Austria. + +8.5 Responsible Courts. The responsible courts in Vienna/Austria will have +exclusive jurisdiction regarding all disputes in connection with this +agreement. + diff --git a/bootloader/usbdrv/License.txt b/bootloader/usbdrv/License.txt new file mode 100644 index 0000000..4460cfb --- /dev/null +++ b/bootloader/usbdrv/License.txt @@ -0,0 +1,361 @@ +OBJECTIVE DEVELOPMENT GmbH's V-USB driver software is distributed under the +terms and conditions of the GNU GPL version 2 or the GNU GPL version 3. It is +your choice whether you apply the terms of version 2 or version 3. The full +text of GPLv2 is included below. In addition to the requirements in the GPL, +we STRONGLY ENCOURAGE you to do the following: + +(1) Publish your entire project on a web site and drop us a note with the URL. +Use the form at http://www.obdev.at/vusb/feedback.html for your submission. + +(2) Adhere to minimum publication standards. Please include AT LEAST: + - a circuit diagram in PDF, PNG or GIF format + - full source code for the host software + - a Readme.txt file in ASCII format which describes the purpose of the + project and what can be found in which directories and which files + - a reference to http://www.obdev.at/vusb/ + +(3) If you improve the driver firmware itself, please give us a free license +to your modifications for our commercial license offerings. + + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/bootloader/usbdrv/Readme.txt b/bootloader/usbdrv/Readme.txt new file mode 100644 index 0000000..970dc66 --- /dev/null +++ b/bootloader/usbdrv/Readme.txt @@ -0,0 +1,172 @@ +This is the Readme file to Objective Development's firmware-only USB driver +for Atmel AVR microcontrollers. For more information please visit +http://www.obdev.at/vusb/ + +This directory contains the USB firmware only. Copy it as-is to your own +project and add all .c and .S files to your project (these files are marked +with an asterisk in the list below). Then copy usbconfig-prototype.h as +usbconfig.h to your project and edit it according to your configuration. + + +TECHNICAL DOCUMENTATION +======================= +The technical documentation (API) for the firmware driver is contained in the +file "usbdrv.h". Please read all of it carefully! Configuration options are +documented in "usbconfig-prototype.h". + +The driver consists of the following files: + Readme.txt ............. The file you are currently reading. + Changelog.txt .......... Release notes for all versions of the driver. + usbdrv.h ............... Driver interface definitions and technical docs. +* usbdrv.c ............... High level language part of the driver. Link this + module to your code! +* usbdrvasm.S ............ Assembler part of the driver. This module is mostly + a stub and includes one of the usbdrvasm*.S files + depending on processor clock. Link this module to + your code! + usbdrvasm*.inc ......... Assembler routines for particular clock frequencies. + Included by usbdrvasm.S, don't link it directly! + asmcommon.inc .......... Common assembler routines. Included by + usbdrvasm*.inc, don't link it directly! + usbconfig-prototype.h .. Prototype for your own usbdrv.h file. +* oddebug.c .............. Debug functions. Only used when DEBUG_LEVEL is + defined to a value greater than 0. Link this module + to your code! + oddebug.h .............. Interface definitions of the debug module. + usbportability.h ....... Header with compiler-dependent stuff. + usbdrvasm.asm .......... Compatibility stub for IAR-C-compiler. Use this + module instead of usbdrvasm.S when you assembler + with IAR's tools. + License.txt ............ Open Source license for this driver. + CommercialLicense.txt .. Optional commercial license for this driver. + USB-ID-FAQ.txt ......... General infos about USB Product- and Vendor-IDs. + USB-IDs-for-free.txt ... List and terms of use for free shared PIDs. + +(*) ... These files should be linked to your project. + + +CPU CORE CLOCK FREQUENCY +======================== +We supply assembler modules for clock frequencies of 12 MHz, 12.8 MHz, 15 MHz, +16 MHz, 16.5 MHz 18 MHz and 20 MHz. Other clock rates are not supported. The +actual clock rate must be configured in usbconfig.h. + +12 MHz Clock +This is the traditional clock rate of V-USB because it's the lowest clock +rate where the timing constraints of the USB spec can be met. + +15 MHz Clock +Similar to 12 MHz, but some NOPs inserted. On the other hand, the higher clock +rate allows for some loops which make the resulting code size somewhat smaller +than the 12 MHz version. + +16 MHz Clock +This clock rate has been added for users of the Arduino board and other +ready-made boards which come with a fixed 16 MHz crystal. It's also an option +if you need the slightly higher clock rate for performance reasons. Since +16 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code +is somewhat tricky and has to insert a leap cycle every third byte. + +12.8 MHz and 16.5 MHz Clock +The assembler modules for these clock rates differ from the other modules +because they have been built for an RC oscillator with only 1% precision. The +receiver code inserts leap cycles to compensate for clock deviations. 1% is +also the precision which can be achieved by calibrating the internal RC +oscillator of the AVR. Please note that only AVRs with internal 64 MHz PLL +oscillator can reach 16.5 MHz with the RC oscillator. This includes the very +popular ATTiny25, ATTiny45, ATTiny85 series as well as the ATTiny26. Almost +all AVRs can reach 12.8 MHz, although this is outside the specified range. + +See the EasyLogger example at http://www.obdev.at/vusb/easylogger.html for +code which calibrates the RC oscillator based on the USB frame clock. + +18 MHz Clock +This module is closer to the USB specification because it performs an on the +fly CRC check for incoming packets. Packets with invalid checksum are +discarded as required by the spec. If you also implement checks for data +PID toggling on application level (see option USB_CFG_CHECK_DATA_TOGGLING +in usbconfig.h for more info), this ensures data integrity. Due to the CRC +tables and alignment requirements, this code is bigger than modules for other +clock rates. To activate this module, you must define USB_CFG_CHECK_CRC to 1 +and USB_CFG_CLOCK_KHZ to 18000 in usbconfig.h. + +20 MHz Clock +This module is for people who won't do it with less than the maximum. Since +20 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code +uses similar tricks as the 16 MHz module to insert leap cycles. + + +USB IDENTIFIERS +=============== +Every USB device needs a vendor- and a product-identifier (VID and PID). VIDs +are obtained from usb.org for a price of 1,500 USD. Once you have a VID, you +can assign PIDs at will. + +Since an entry level cost of 1,500 USD is too high for most small companies +and hobbyists, we provide some VID/PID pairs for free. See the file +USB-IDs-for-free.txt for details. + +Objective Development also has some license offerings which include product +IDs. See http://www.obdev.at/vusb/ for details. + + +DEVELOPMENT SYSTEM +================== +This driver has been developed and optimized for the GNU compiler version 3 +and 4. We recommend that you use the GNU compiler suite because it is freely +available. V-USB has also been ported to the IAR compiler and assembler. It +has been tested with IAR 4.10B/W32 and 4.12A/W32 on an ATmega8 with the +"small" and "tiny" memory model. Not every release is tested with IAR CC and +the driver may therefore fail to compile with IAR. Please note that gcc is +more efficient for usbdrv.c because this module has been deliberately +optimized for gcc. + +Gcc version 3 produces smaller code than version 4 due to new optimizing +capabilities which don't always improve things on 8 bit CPUs. The code size +generated by gcc 4 can be reduced with the compiler options +-fno-move-loop-invariants, -fno-tree-scev-cprop and +-fno-inline-small-functions in addition to -Os. On devices with more than +8k of flash memory, we also recommend the linker option --relax (written as +-Wl,--relax for gcc) to convert absolute calls into relative where possible. + +For more information about optimizing options see: + + http://www.tty1.net/blog/2008-04-29-avr-gcc-optimisations_en.html + +These optimizations are good for gcc 4.x. Version 3.x of gcc does not support +most of these options and produces good code anyway. + + +USING V-USB FOR FREE +==================== +The AVR firmware driver is published under the GNU General Public License +Version 2 (GPL2) and the GNU General Public License Version 3 (GPL3). It is +your choice whether you apply the terms of version 2 or version 3. + +If you decide for the free GPL2 or GPL3, we STRONGLY ENCOURAGE you to do the +following things IN ADDITION to the obligations from the GPL: + +(1) Publish your entire project on a web site and drop us a note with the URL. +Use the form at http://www.obdev.at/vusb/feedback.html for your submission. +If you don't have a web site, you can publish the project in obdev's +documentation wiki at +http://www.obdev.at/goto.php?t=vusb-wiki&p=hosted-projects. + +(2) Adhere to minimum publication standards. Please include AT LEAST: + - a circuit diagram in PDF, PNG or GIF format + - full source code for the host software + - a Readme.txt file in ASCII format which describes the purpose of the + project and what can be found in which directories and which files + - a reference to http://www.obdev.at/vusb/ + +(3) If you improve the driver firmware itself, please give us a free license +to your modifications for our commercial license offerings. + + +COMMERCIAL LICENSES FOR V-USB +============================= +If you don't want to publish your source code under the terms of the GPL, +you can simply pay money for V-USB. As an additional benefit you get +USB PIDs for free, reserved exclusively to you. See the file +"CommercialLicense.txt" for details. + diff --git a/bootloader/usbdrv/USB-ID-FAQ.txt b/bootloader/usbdrv/USB-ID-FAQ.txt new file mode 100644 index 0000000..d1de8fb --- /dev/null +++ b/bootloader/usbdrv/USB-ID-FAQ.txt @@ -0,0 +1,149 @@ +Version 2009-08-22 + +========================== +WHY DO WE NEED THESE IDs? +========================== + +USB is more than a low level protocol for data transport. It also defines a +common set of requests which must be understood by all devices. And as part +of these common requests, the specification defines data structures, the +USB Descriptors, which are used to describe the properties of the device. + +From the perspective of an operating system, it is therefore possible to find +out basic properties of a device (such as e.g. the manufacturer and the name +of the device) without a device-specific driver. This is essential because +the operating system can choose a driver to load based on this information +(Plug-And-Play). + +Among the most important properties in the Device Descriptor are the USB +Vendor- and Product-ID. Both are 16 bit integers. The most simple form of +driver matching is based on these IDs. The driver announces the Vendor- and +Product-IDs of the devices it can handle and the operating system loads the +appropriate driver when the device is connected. + +It is obvious that this technique only works if the pair Vendor- plus +Product-ID is unique: Only devices which require the same driver can have the +same pair of IDs. + + +===================================================== +HOW DOES THE USB STANDARD ENSURE THAT IDs ARE UNIQUE? +===================================================== + +Since it is so important that USB IDs are unique, the USB Implementers Forum, +Inc. (usb.org) needs a way to enforce this legally. It is not forbidden by +law to build a device and assign it any random numbers as IDs. Usb.org +therefore needs an agreement to regulate the use of USB IDs. The agreement +binds only parties who agreed to it, of course. Everybody else is free to use +any numbers for their IDs. + +So how can usb.org ensure that every manufacturer of USB devices enters into +an agreement with them? They do it via trademark licensing. Usb.org has +registered the trademark "USB", all associated logos and related terms. If +you want to put an USB logo on your product or claim that it is USB +compliant, you must license these trademarks from usb.org. And this is where +you enter into an agreement. See the "USB-IF Trademark License Agreement and +Usage Guidelines for the USB-IF Logo" at +http://www.usb.org/developers/logo_license/. + +Licensing the USB trademarks requires that you buy a USB Vendor-ID from +usb.org (one-time fee of ca. 2,000 USD), that you become a member of usb.org +(yearly fee of ca. 4,000 USD) and that you meet all the technical +specifications from the USB spec. + +This means that most hobbyists and small companies will never be able to +become USB compliant, just because membership is so expensive. And you can't +be compliant with a driver based on V-USB anyway, because the AVR's port pins +don't meet the electrical specifications for USB. So, in principle, all +hobbyists and small companies are free to choose any random numbers for their +IDs. They have nothing to lose... + +There is one exception worth noting, though: If you use a sub-component which +implements USB, the vendor of the sub-components may guarantee USB +compliance. This might apply to some or all of FTDI's solutions. + + +======================================================================= +WHY SHOULD YOU OBTAIN USB IDs EVEN IF YOU DON'T LICENSE USB TRADEMARKS? +======================================================================= + +You have learned in the previous section that you are free to choose any +numbers for your IDs anyway. So why not do exactly this? There is still the +technical issue. If you choose IDs which are already in use by somebody else, +operating systems will load the wrong drivers and your device won't work. +Even if you choose IDs which are not currently in use, they may be in use in +the next version of the operating system or even after an automatic update. + +So what you need is a pair of Vendor- and Product-IDs for which you have the +guarantee that no USB compliant product uses them. This implies that no +operating system will ever ship with drivers responsible for these IDs. + + +============================================== +HOW DOES OBJECTIVE DEVELOPMENT HANDLE USB IDs? +============================================== + +Objective Development gives away pairs of USB-IDs with their V-USB licenses. +In order to ensure that these IDs are unique, Objective Development has an +agreement with the company/person who has bought the USB Vendor-ID from +usb.org. This agreement ensures that a range of USB Product-IDs is reserved +for assignment by Objective Development and that the owner of the Vendor-ID +won't give it to anybody else. + +This means that you have to trust three parties to ensure uniqueness of +your IDs: + + - Objective Development, that they don't give the same PID to more than + one person. + - The owner of the Vendor-ID that they don't assign PIDs from the range + assigned to Objective Development to anybody else. + - Usb.org that they don't assign the same Vendor-ID a second time. + + +================================== +WHO IS THE OWNER OF THE VENDOR-ID? +================================== + +Objective Development has obtained ranges of USB Product-IDs under two +Vendor-IDs: Under Vendor-ID 5824 from Wouter van Ooijen (Van Ooijen +Technische Informatica, www.voti.nl) and under Vendor-ID 8352 from Jason +Kotzin (Clay Logic, www.claylogic.com). Both VID owners have received their +Vendor-ID directly from usb.org. + + +========================================================================= +CAN I USE USB-IDs FROM OBJECTIVE DEVELOPMENT WITH OTHER DRIVERS/HARDWARE? +========================================================================= + +The short answer is: Yes. All you get is a guarantee that the IDs are never +assigned to anybody else. What more do you need? + + +============================ +WHAT ABOUT SHARED ID PAIRS? +============================ + +Objective Development has reserved some PID/VID pairs for shared use. You +have no guarantee of uniqueness for them, except that no USB compliant device +uses them. In order to avoid technical problems, we must ensure that all +devices with the same pair of IDs use the same driver on kernel level. For +details, see the file USB-IDs-for-free.txt. + + +====================================================== +I HAVE HEARD THAT SUB-LICENSING OF USB-IDs IS ILLEGAL? +====================================================== + +A 16 bit integer number cannot be protected by copyright laws. It is not +sufficiently complex. And since none of the parties involved entered into the +USB-IF Trademark License Agreement, we are not bound by this agreement. So +there is no reason why it should be illegal to sub-license USB-IDs. + + +============================================= +WHO IS LIABLE IF THERE ARE INCOMPATIBILITIES? +============================================= + +Objective Development disclaims all liabilities which might arise from the +assignment of IDs. If you guarantee product features to your customers +without proper disclaimer, YOU are liable for that. diff --git a/bootloader/usbdrv/USB-IDs-for-free.txt b/bootloader/usbdrv/USB-IDs-for-free.txt new file mode 100644 index 0000000..2f4d59a --- /dev/null +++ b/bootloader/usbdrv/USB-IDs-for-free.txt @@ -0,0 +1,148 @@ +Version 2009-08-22 + +=========================== +FREE USB-IDs FOR SHARED USE +=========================== + +Objective Development has reserved a set of USB Product-IDs for use according +to the guidelines outlined below. For more information about the concept of +USB IDs please see the file USB-ID-FAQ.txt. Objective Development guarantees +that the IDs listed below are not used by any USB compliant devices. + + +==================== +MECHANISM OF SHARING +==================== + +From a technical point of view, two different devices can share the same USB +Vendor- and Product-ID if they require the same driver on operating system +level. We make use of this fact by assigning separate IDs for various device +classes. On application layer, devices must be distinguished by their textual +name or serial number. We offer separate sets of IDs for discrimination by +textual name and for serial number. + +Examples for shared use of USB IDs are included with V-USB in the "examples" +subdirectory. + + +====================================== +IDs FOR DISCRIMINATION BY TEXTUAL NAME +====================================== + +If you use one of the IDs listed below, your device and host-side software +must conform to these rules: + +(1) The USB device MUST provide a textual representation of the manufacturer +and product identification. The manufacturer identification MUST be available +at least in USB language 0x0409 (English/US). + +(2) The textual manufacturer identification MUST contain either an Internet +domain name (e.g. "mycompany.com") registered and owned by you, or an e-mail +address under your control (e.g. "myname@gmx.net"). You can embed the domain +name or e-mail address in any string you like, e.g. "Objective Development +http://www.obdev.at/vusb/". + +(3) You are responsible for retaining ownership of the domain or e-mail +address for as long as any of your products are in use. + +(4) You may choose any string for the textual product identification, as long +as this string is unique within the scope of your textual manufacturer +identification. + +(5) Application side device look-up MUST be based on the textual manufacturer +and product identification in addition to VID/PID matching. The driver +matching MUST be a comparison of the entire strings, NOT a sub-string match. + +(6) For devices which implement a particular USB device class (e.g. HID), the +operating system's default class driver MUST be used. If an operating system +driver for Vendor Class devices is needed, this driver must be libusb or +libusb-win32 (see http://libusb.org/ and +http://libusb-win32.sourceforge.net/). + +Table if IDs for discrimination by textual name: + +PID dec (hex) | VID dec (hex) | Description of use +==============+===============+============================================ +1500 (0x05dc) | 5824 (0x16c0) | For Vendor Class devices with libusb +--------------+---------------+-------------------------------------------- +1503 (0x05df) | 5824 (0x16c0) | For generic HID class devices (which are + | | NOT mice, keyboards or joysticks) +--------------+---------------+-------------------------------------------- +1505 (0x05e1) | 5824 (0x16c0) | For CDC-ACM class devices (modems) +--------------+---------------+-------------------------------------------- +1508 (0x05e4) | 5824 (0x16c0) | For MIDI class devices +--------------+---------------+-------------------------------------------- + +Note that Windows caches the textual product- and vendor-description for +mice, keyboards and joysticks. Name-bsed discrimination is therefore not +recommended for these device classes. + + +======================================= +IDs FOR DISCRIMINATION BY SERIAL NUMBER +======================================= + +If you use one of the IDs listed below, your device and host-side software +must conform to these rules: + +(1) The USB device MUST provide a textual representation of the serial +number. The serial number string MUST be available at least in USB language +0x0409 (English/US). + +(2) The serial number MUST start with either an Internet domain name (e.g. +"mycompany.com") registered and owned by you, or an e-mail address under your +control (e.g. "myname@gmx.net"), both terminated with a colon (":") character. +You MAY append any string you like for further discrimination of your devices. + +(3) You are responsible for retaining ownership of the domain or e-mail +address for as long as any of your products are in use. + +(5) Application side device look-up MUST be based on the serial number string +in addition to VID/PID matching. The matching must start at the first +character of the serial number string and include the colon character +terminating your domain or e-mail address. It MAY stop anywhere after that. + +(6) For devices which implement a particular USB device class (e.g. HID), the +operating system's default class driver MUST be used. If an operating system +driver for Vendor Class devices is needed, this driver must be libusb or +libusb-win32 (see http://libusb.org/ and +http://libusb-win32.sourceforge.net/). + +Table if IDs for discrimination by serial number string: + +PID dec (hex) | VID dec (hex) | Description of use +===============+===============+=========================================== +10200 (0x27d8) | 5824 (0x16c0) | For Vendor Class devices with libusb +---------------+---------------+------------------------------------------- +10201 (0x27d9) | 5824 (0x16c0) | For generic HID class devices (which are + | | NOT mice, keyboards or joysticks) +---------------+---------------+------------------------------------------- +10202 (0x27da) | 5824 (0x16c0) | For USB Mice +---------------+---------------+------------------------------------------- +10203 (0x27db) | 5824 (0x16c0) | For USB Keyboards +---------------+---------------+------------------------------------------- +10204 (0x27db) | 5824 (0x16c0) | For USB Joysticks +---------------+---------------+------------------------------------------- +10205 (0x27dc) | 5824 (0x16c0) | For CDC-ACM class devices (modems) +---------------+---------------+------------------------------------------- +10206 (0x27dd) | 5824 (0x16c0) | For MIDI class devices +---------------+---------------+------------------------------------------- + + +================= +ORIGIN OF USB-IDs +================= + +OBJECTIVE DEVELOPMENT Software GmbH has obtained all VID/PID pairs listed +here from Wouter van Ooijen (see www.voti.nl) for exclusive disposition. +Wouter van Ooijen has obtained the VID from the USB Implementers Forum, Inc. +(see www.usb.org). The VID is registered for the company name "Van Ooijen +Technische Informatica". + + +========== +DISCLAIMER +========== + +OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any +problems which are caused by the shared use of these VID/PID pairs. diff --git a/bootloader/usbdrv/USBID-License.txt b/bootloader/usbdrv/USBID-License.txt new file mode 100644 index 0000000..984c9ee --- /dev/null +++ b/bootloader/usbdrv/USBID-License.txt @@ -0,0 +1,146 @@ +Royalty-Free Non-Exclusive License USB Product-ID +================================================= + +Version 2008-04-07 + +OBJECTIVE DEVELOPMENT Software GmbH hereby grants you the non-exclusive +right to use three USB.org vendor-ID (VID) / product-ID (PID) pairs with +products based on Objective Development's firmware-only USB driver for +Atmel AVR microcontrollers: + + * VID = 5824 (=0x16c0) / PID = 1500 (=0x5dc) for devices implementing no + USB device class (vendor-class devices with USB class = 0xff). Devices + using this pair will be referred to as "VENDOR CLASS" devices. + + * VID = 5824 (=0x16c0) / PID = 1503 (=0x5df) for HID class devices + (excluding mice and keyboards). Devices using this pair will be referred + to as "HID CLASS" devices. + + * VID = 5824 (=0x16c0) / PID = 1505 (=0x5e1) for CDC class modem devices + Devices using this pair will be referred to as "CDC-ACM CLASS" devices. + + * VID = 5824 (=0x16c0) / PID = 1508 (=0x5e4) for MIDI class devices + Devices using this pair will be referred to as "MIDI CLASS" devices. + +Since the granted right is non-exclusive, the same VID/PID pairs may be +used by many companies and individuals for different products. To avoid +conflicts, your device and host driver software MUST adhere to the rules +outlined below. + +OBJECTIVE DEVELOPMENT Software GmbH has licensed these VID/PID pairs from +Wouter van Ooijen (see www.voti.nl), who has licensed the VID from the USB +Implementers Forum, Inc. (see www.usb.org). The VID is registered for the +company name "Van Ooijen Technische Informatica". + + +RULES AND RESTRICTIONS +====================== + +(1) The USB device MUST provide a textual representation of the +manufacturer and product identification. The manufacturer identification +MUST be available at least in USB language 0x0409 (English/US). + +(2) The textual manufacturer identification MUST contain either an Internet +domain name (e.g. "mycompany.com") registered and owned by you, or an +e-mail address under your control (e.g. "myname@gmx.net"). You can embed +the domain name or e-mail address in any string you like, e.g. "Objective +Development http://www.obdev.at/avrusb/". + +(3) You are responsible for retaining ownership of the domain or e-mail +address for as long as any of your products are in use. + +(4) You may choose any string for the textual product identification, as +long as this string is unique within the scope of your textual manufacturer +identification. + +(5) Matching of device-specific drivers MUST be based on the textual +manufacturer and product identification in addition to the usual VID/PID +matching. This means that operating system features which are based on +VID/PID matching only (e.g. Windows kernel level drivers, automatic actions +when the device is plugged in etc) MUST NOT be used. The driver matching +MUST be a comparison of the entire strings, NOT a sub-string match. For +CDC-ACM CLASS and MIDI CLASS devices, a generic class driver should be used +and the matching is based on the USB device class. + +(6) The extent to which VID/PID matching is allowed for non device-specific +drivers or features depends on the operating system and particular VID/PID +pair used: + + * Mac OS X, Linux, FreeBSD and other Unixes: No VID/PID matching is + required and hence no VID/PID-only matching is allowed at all. + + * Windows: The operating system performs VID/PID matching for the kernel + level driver. You are REQUIRED to use libusb-win32 (see + http://libusb-win32.sourceforge.net/) as the kernel level driver for + VENDOR CLASS devices. HID CLASS devices all use the generic HID class + driver shipped with Windows, except mice and keyboards. You therefore + MUST NOT use any of the shared VID/PID pairs for mice or keyboards. + CDC-ACM CLASS devices require a ".inf" file which matches on the VID/PID + pair. This ".inf" file MUST load the "usbser" driver to configure the + device as modem (COM-port). + +(7) OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any +problems which are caused by the shared use of these VID/PID pairs. You +have been warned that the sharing of VID/PID pairs may cause problems. If +you want to avoid them, get your own VID/PID pair for exclusive use. + + +HOW TO IMPLEMENT THESE RULES +============================ + +The following rules are for VENDOR CLASS and HID CLASS devices. CDC-ACM +CLASS and MIDI CLASS devices use the operating system's class driver and +don't need a custom driver. + +The host driver MUST iterate over all devices with the given VID/PID +numbers in their device descriptors and query the string representation for +the manufacturer name in USB language 0x0409 (English/US). It MUST compare +the ENTIRE string with your textual manufacturer identification chosen in +(2) above. A substring search for your domain or e-mail address is NOT +acceptable. The driver MUST NOT touch the device (other than querying the +descriptors) unless the strings match. + +For all USB devices with matching VID/PID and textual manufacturer +identification, the host driver must query the textual product +identification and string-compare it with the name of the product it can +control. It may only initialize the device if the product matches exactly. + +Objective Development provides examples for these matching rules with the +"PowerSwitch" project (using libusb) and with the "Automator" project +(using Windows calls on Windows and libusb on Unix). + + +Technical Notes: +================ + +Sharing the same VID/PID pair among devices is possible as long as ALL +drivers which match the VID/PID also perform matching on the textual +identification strings. This is easy on all operating systems except +Windows, since Windows establishes a static connection between the VID/PID +pair and a kernel level driver. All devices with the same VID/PID pair must +therefore use THE SAME kernel level driver. + +We therefore demand that you use libusb-win32 for VENDOR CLASS devices. +This is a generic kernel level driver which allows all types of USB access +for user space applications. This is only a partial solution of the +problem, though, because different device drivers may come with different +versions of libusb-win32 and they may not work with the libusb version of +the respective other driver. You are therefore encouraged to test your +driver against a broad range of libusb-win32 versions. Do not use new +features in new versions, or check for their existence before you use them. +When a new libusb-win32 becomes available, make sure that your driver is +compatible with it. + +For HID CLASS devices it is necessary that all those devices bind to the +same kernel driver: Microsoft's generic USB HID driver. This is true for +all HID devices except those with a specialized driver. Currently, the only +HIDs with specialized drivers are mice and keyboards. You therefore MUST +NOT use a shared VID/PID with mouse and keyboard devices. + +Sharing the same VID/PID among different products is unusual and probably +violates the USB specification. If you do it, you do it at your own risk. + +To avoid possible incompatibilities, we highly recommend that you get your +own VID/PID pair if you intend to sell your product. Objective +Development's commercial licenses for AVR-USB include a PID for +unrestricted exclusive use. diff --git a/bootloader/usbdrv/asmcommon.inc b/bootloader/usbdrv/asmcommon.inc new file mode 100644 index 0000000..07d692b --- /dev/null +++ b/bootloader/usbdrv/asmcommon.inc @@ -0,0 +1,188 @@ +/* Name: asmcommon.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2007-11-05 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * Revision: $Id$ + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file contains assembler code which is shared among the USB driver +implementations for different CPU cocks. Since the code must be inserted +in the middle of the module, it's split out into this file and #included. + +Jump destinations called from outside: + sofError: Called when no start sequence was found. + se0: Called when a package has been successfully received. + overflow: Called when receive buffer overflows. + doReturn: Called after sending data. + +Outside jump destinations used by this module: + waitForJ: Called to receive an already arriving packet. + sendAckAndReti: + sendNakAndReti: + sendCntAndReti: + usbSendAndReti: + +The following macros must be defined before this file is included: + .macro POP_STANDARD + .endm + .macro POP_RETI + .endm +*/ + +#define token x1 + +overflow: + ldi x2, 1< 0 + +#warning "Never compile production devices with debugging enabled" + +static void uartPutc(char c) +{ + while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ + ODDBG_UDR = c; +} + +static uchar hexAscii(uchar h) +{ + h &= 0xf; + if(h >= 10) + h += 'a' - (uchar)10 - '0'; + h += '0'; + return h; +} + +static void printHex(uchar c) +{ + uartPutc(hexAscii(c >> 4)); + uartPutc(hexAscii(c)); +} + +void odDebug(uchar prefix, uchar *data, uchar len) +{ + printHex(prefix); + uartPutc(':'); + while(len--){ + uartPutc(' '); + printHex(*data++); + } + uartPutc('\r'); + uartPutc('\n'); +} + +#endif diff --git a/bootloader/usbdrv/oddebug.h b/bootloader/usbdrv/oddebug.h new file mode 100644 index 0000000..d61309d --- /dev/null +++ b/bootloader/usbdrv/oddebug.h @@ -0,0 +1,123 @@ +/* Name: oddebug.h + * Project: AVR library + * Author: Christian Starkjohann + * Creation Date: 2005-01-16 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * This Revision: $Id: oddebug.h 692 2008-11-07 15:07:40Z cs $ + */ + +#ifndef __oddebug_h_included__ +#define __oddebug_h_included__ + +/* +General Description: +This module implements a function for debug logs on the serial line of the +AVR microcontroller. Debugging can be configured with the define +'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging +calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is +2, DBG1 and DBG2 logs will be printed. + +A debug log consists of a label ('prefix') to indicate which debug log created +the output and a memory block to dump in hex ('data' and 'len'). +*/ + + +#ifndef F_CPU +# define F_CPU 12000000 /* 12 MHz */ +#endif + +/* make sure we have the UART defines: */ +#include "usbportability.h" + +#ifndef uchar +# define uchar unsigned char +#endif + +#if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ +# warning "Debugging disabled because device has no UART" +# undef DEBUG_LEVEL +#endif + +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 0 +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +# define DBG1(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG1(prefix, data, len) +#endif + +#if DEBUG_LEVEL > 1 +# define DBG2(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG2(prefix, data, len) +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +extern void odDebug(uchar prefix, uchar *data, uchar len); + +/* Try to find our control registers; ATMEL likes to rename these */ + +#if defined UBRR +# define ODDBG_UBRR UBRR +#elif defined UBRRL +# define ODDBG_UBRR UBRRL +#elif defined UBRR0 +# define ODDBG_UBRR UBRR0 +#elif defined UBRR0L +# define ODDBG_UBRR UBRR0L +#endif + +#if defined UCR +# define ODDBG_UCR UCR +#elif defined UCSRB +# define ODDBG_UCR UCSRB +#elif defined UCSR0B +# define ODDBG_UCR UCSR0B +#endif + +#if defined TXEN +# define ODDBG_TXEN TXEN +#else +# define ODDBG_TXEN TXEN0 +#endif + +#if defined USR +# define ODDBG_USR USR +#elif defined UCSRA +# define ODDBG_USR UCSRA +#elif defined UCSR0A +# define ODDBG_USR UCSR0A +#endif + +#if defined UDRE +# define ODDBG_UDRE UDRE +#else +# define ODDBG_UDRE UDRE0 +#endif + +#if defined UDR +# define ODDBG_UDR UDR +#elif defined UDR0 +# define ODDBG_UDR UDR0 +#endif + +static inline void odDebugInit(void) +{ + ODDBG_UCR |= (1<len & 0x10){ /* packet buffer was empty */ + txStatus->buffer[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ + }else{ + txStatus->len = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ + } + p = txStatus->buffer + 1; + i = len; + do{ /* if len == 0, we still copy 1 byte, but that's no problem */ + *p++ = *data++; + }while(--i > 0); /* loop control at the end is 2 bytes shorter than at beginning */ + usbCrc16Append(&txStatus->buffer[1], len); + txStatus->len = len + 4; /* len must be given including sync byte */ + DBG2(0x21 + (((int)txStatus >> 3) & 3), txStatus->buffer, len + 3); +} + +USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len) +{ + usbGenericSetInterrupt(data, len, &usbTxStatus1); +} +#endif + +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len) +{ + usbGenericSetInterrupt(data, len, &usbTxStatus3); +} +#endif +#endif /* USB_CFG_SUPPRESS_INTR_CODE */ + +/* ------------------ utilities for code following below ------------------- */ + +/* Use defines for the switch statement so that we can choose between an + * if()else if() and a switch/case based implementation. switch() is more + * efficient for a LARGE set of sequential choices, if() is better in all other + * cases. + */ +#if USB_CFG_USE_SWITCH_STATEMENT +# define SWITCH_START(cmd) switch(cmd){{ +# define SWITCH_CASE(value) }break; case (value):{ +# define SWITCH_CASE2(v1,v2) }break; case (v1): case(v2):{ +# define SWITCH_CASE3(v1,v2,v3) }break; case (v1): case(v2): case(v3):{ +# define SWITCH_DEFAULT }break; default:{ +# define SWITCH_END }} +#else +# define SWITCH_START(cmd) {uchar _cmd = cmd; if(0){ +# define SWITCH_CASE(value) }else if(_cmd == (value)){ +# define SWITCH_CASE2(v1,v2) }else if(_cmd == (v1) || _cmd == (v2)){ +# define SWITCH_CASE3(v1,v2,v3) }else if(_cmd == (v1) || _cmd == (v2) || (_cmd == v3)){ +# define SWITCH_DEFAULT }else{ +# define SWITCH_END }} +#endif + +#ifndef USB_RX_USER_HOOK +#define USB_RX_USER_HOOK(data, len) +#endif +#ifndef USB_SET_ADDRESS_HOOK +#define USB_SET_ADDRESS_HOOK() +#endif + +/* ------------------------------------------------------------------------- */ + +/* We use if() instead of #if in the macro below because #if can't be used + * in macros and the compiler optimizes constant conditions anyway. + * This may cause problems with undefined symbols if compiled without + * optimizing! + */ +#define GET_DESCRIPTOR(cfgProp, staticName) \ + if(cfgProp){ \ + if((cfgProp) & USB_PROP_IS_RAM) \ + flags = 0; \ + if((cfgProp) & USB_PROP_IS_DYNAMIC){ \ + len = usbFunctionDescriptor(rq); \ + }else{ \ + len = USB_PROP_LENGTH(cfgProp); \ + usbMsgPtr = (uchar *)(staticName); \ + } \ + } + +/* usbDriverDescriptor() is similar to usbFunctionDescriptor(), but used + * internally for all types of descriptors. + */ +static inline usbMsgLen_t usbDriverDescriptor(usbRequest_t *rq) +{ +usbMsgLen_t len = 0; +uchar flags = USB_FLG_MSGPTR_IS_ROM; + + SWITCH_START(rq->wValue.bytes[1]) + SWITCH_CASE(USBDESCR_DEVICE) /* 1 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) + SWITCH_CASE(USBDESCR_CONFIG) /* 2 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) + SWITCH_CASE(USBDESCR_STRING) /* 3 */ +#if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC + if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) + flags = 0; + len = usbFunctionDescriptor(rq); +#else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ + SWITCH_START(rq->wValue.bytes[0]) + SWITCH_CASE(0) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) + SWITCH_CASE(1) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) + SWITCH_CASE(2) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_PRODUCT, usbDescriptorStringDevice) + SWITCH_CASE(3) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) + SWITCH_DEFAULT + if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ + len = usbFunctionDescriptor(rq); + } + SWITCH_END +#endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ +#if USB_CFG_DESCR_PROPS_HID_REPORT /* only support HID descriptors if enabled */ + SWITCH_CASE(USBDESCR_HID) /* 0x21 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) + SWITCH_CASE(USBDESCR_HID_REPORT)/* 0x22 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) +#endif + SWITCH_DEFAULT + if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ + len = usbFunctionDescriptor(rq); + } + SWITCH_END + usbMsgFlags = flags; + return len; +} + +/* ------------------------------------------------------------------------- */ + +/* usbDriverSetup() is similar to usbFunctionSetup(), but it's used for + * standard requests instead of class and custom requests. + */ +static inline usbMsgLen_t usbDriverSetup(usbRequest_t *rq) +{ +uchar len = 0, *dataPtr = usbTxBuf + 9; /* there are 2 bytes free space at the end of the buffer */ +uchar value = rq->wValue.bytes[0]; +#if USB_CFG_IMPLEMENT_HALT +uchar index = rq->wIndex.bytes[0]; +#endif + + dataPtr[0] = 0; /* default reply common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ + SWITCH_START(rq->bRequest) + SWITCH_CASE(USBRQ_GET_STATUS) /* 0 */ + uchar recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ + if(USB_CFG_IS_SELF_POWERED && recipient == USBRQ_RCPT_DEVICE) + dataPtr[0] = USB_CFG_IS_SELF_POWERED; +#if USB_CFG_IMPLEMENT_HALT + if(recipient == USBRQ_RCPT_ENDPOINT && index == 0x81) /* request status for endpoint 1 */ + dataPtr[0] = usbTxLen1 == USBPID_STALL; +#endif + dataPtr[1] = 0; + len = 2; +#if USB_CFG_IMPLEMENT_HALT + SWITCH_CASE2(USBRQ_CLEAR_FEATURE, USBRQ_SET_FEATURE) /* 1, 3 */ + if(value == 0 && index == 0x81){ /* feature 0 == HALT for endpoint == 1 */ + usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; + usbResetDataToggling(); + } +#endif + SWITCH_CASE(USBRQ_SET_ADDRESS) /* 5 */ + usbNewDeviceAddr = value; + USB_SET_ADDRESS_HOOK(); + SWITCH_CASE(USBRQ_GET_DESCRIPTOR) /* 6 */ + len = usbDriverDescriptor(rq); + goto skipMsgPtrAssignment; + SWITCH_CASE(USBRQ_GET_CONFIGURATION) /* 8 */ + dataPtr = &usbConfiguration; /* send current configuration value */ + len = 1; + SWITCH_CASE(USBRQ_SET_CONFIGURATION) /* 9 */ + usbConfiguration = value; + usbResetStall(); + SWITCH_CASE(USBRQ_GET_INTERFACE) /* 10 */ + len = 1; +#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE + SWITCH_CASE(USBRQ_SET_INTERFACE) /* 11 */ + usbResetDataToggling(); + usbResetStall(); +#endif + SWITCH_DEFAULT /* 7=SET_DESCRIPTOR, 12=SYNC_FRAME */ + /* Should we add an optional hook here? */ + SWITCH_END + usbMsgPtr = dataPtr; +skipMsgPtrAssignment: + return len; +} + +/* ------------------------------------------------------------------------- */ + +/* usbProcessRx() is called for every message received by the interrupt + * routine. It distinguishes between SETUP and DATA packets and processes + * them accordingly. + */ +static inline void usbProcessRx(uchar *data, uchar len) +{ +usbRequest_t *rq = (void *)data; + +/* usbRxToken can be: + * 0x2d 00101101 (USBPID_SETUP for setup data) + * 0xe1 11100001 (USBPID_OUT: data phase of setup transfer) + * 0...0x0f for OUT on endpoint X + */ + DBG2(0x10 + (usbRxToken & 0xf), data, len + 2); /* SETUP=1d, SETUP-DATA=11, OUTx=1x */ + USB_RX_USER_HOOK(data, len) +#if USB_CFG_IMPLEMENT_FN_WRITEOUT + if(usbRxToken < 0x10){ /* OUT to endpoint != 0: endpoint number in usbRxToken */ + usbFunctionWriteOut(data, len); + return; + } +#endif + if(usbRxToken == (uchar)USBPID_SETUP){ + if(len != 8) /* Setup size must be always 8 bytes. Ignore otherwise. */ + return; + usbMsgLen_t replyLen; + usbTxBuf[0] = USBPID_DATA0; /* initialize data toggling */ + usbTxLen = USBPID_NAK; /* abort pending transmit */ + usbMsgFlags = 0; + uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; + if(type != USBRQ_TYPE_STANDARD){ /* standard requests are handled by driver */ + replyLen = usbFunctionSetup(data); + }else{ + replyLen = usbDriverSetup(rq); + } +#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE + if(replyLen == USB_NO_MSG){ /* use user-supplied read/write function */ + /* do some conditioning on replyLen, but on IN transfers only */ + if((rq->bmRequestType & USBRQ_DIR_MASK) != USBRQ_DIR_HOST_TO_DEVICE){ + if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ + replyLen = rq->wLength.bytes[0]; + }else{ + replyLen = rq->wLength.word; + } + } + usbMsgFlags = USB_FLG_USE_USER_RW; + }else /* The 'else' prevents that we limit a replyLen of USB_NO_MSG to the maximum transfer len. */ +#endif + if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ + if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */ + replyLen = rq->wLength.bytes[0]; + }else{ + if(replyLen > rq->wLength.word) /* limit length to max */ + replyLen = rq->wLength.word; + } + usbMsgLen = replyLen; + }else{ /* usbRxToken must be USBPID_OUT, which means data phase of setup (control-out) */ +#if USB_CFG_IMPLEMENT_FN_WRITE + if(usbMsgFlags & USB_FLG_USE_USER_RW){ + uchar rval = usbFunctionWrite(data, len); + if(rval == 0xff){ /* an error occurred */ + usbTxLen = USBPID_STALL; + }else if(rval != 0){ /* This was the final package */ + usbMsgLen = 0; /* answer with a zero-sized data packet */ + } + } +#endif + } +} + +/* ------------------------------------------------------------------------- */ + +/* This function is similar to usbFunctionRead(), but it's also called for + * data handled automatically by the driver (e.g. descriptor reads). + */ +static uchar usbDeviceRead(uchar *data, uchar len) +{ + if(len > 0){ /* don't bother app with 0 sized reads */ +#if USB_CFG_IMPLEMENT_FN_READ + if(usbMsgFlags & USB_FLG_USE_USER_RW){ + len = usbFunctionRead(data, len); + }else +#endif + { + uchar i = len, *r = usbMsgPtr; + if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ + do{ + uchar c = USB_READ_FLASH(r); /* assign to char size variable to enforce byte ops */ + *data++ = c; + r++; + }while(--i); + }else{ /* RAM data */ + do{ + *data++ = *r++; + }while(--i); + } + usbMsgPtr = r; + } + } + return len; +} + +/* ------------------------------------------------------------------------- */ + +/* usbBuildTxBlock() is called when we have data to transmit and the + * interrupt routine's transmit buffer is empty. + */ +static inline void usbBuildTxBlock(void) +{ +usbMsgLen_t wantLen; +uchar len; + + wantLen = usbMsgLen; + if(wantLen > 8) + wantLen = 8; + usbMsgLen -= wantLen; + usbTxBuf[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* DATA toggling */ + len = usbDeviceRead(usbTxBuf + 1, wantLen); + if(len <= 8){ /* valid data packet */ + usbCrc16Append(&usbTxBuf[1], len); + len += 4; /* length including sync byte */ + if(len < 12) /* a partial package identifies end of message */ + usbMsgLen = USB_NO_MSG; + }else{ + len = USBPID_STALL; /* stall the endpoint */ + usbMsgLen = USB_NO_MSG; + } + usbTxLen = len; + DBG2(0x20, usbTxBuf, len-1); +} + +/* ------------------------------------------------------------------------- */ + +static inline void usbHandleResetHook(uchar notResetState) +{ +#ifdef USB_RESET_HOOK +static uchar wasReset; +uchar isReset = !notResetState; + + if(wasReset != isReset){ + USB_RESET_HOOK(isReset); + wasReset = isReset; + } +#endif +} + +/* ------------------------------------------------------------------------- */ + +USB_PUBLIC void usbPoll(void) +{ +schar len; +uchar i; + + len = usbRxLen - 3; + if(len >= 0){ +/* We could check CRC16 here -- but ACK has already been sent anyway. If you + * need data integrity checks with this driver, check the CRC in your app + * code and report errors back to the host. Since the ACK was already sent, + * retries must be handled on application level. + * unsigned crc = usbCrc16(buffer + 1, usbRxLen - 3); + */ + usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len); +#if USB_CFG_HAVE_FLOWCONTROL + if(usbRxLen > 0) /* only mark as available if not inactivated */ + usbRxLen = 0; +#else + usbRxLen = 0; /* mark rx buffer as available */ +#endif + } + if(usbTxLen & 0x10){ /* transmit system idle */ + if(usbMsgLen != USB_NO_MSG){ /* transmit data pending? */ + usbBuildTxBlock(); + } + } + for(i = 20; i > 0; i--){ + uchar usbLineStatus = USBIN & USBMASK; + if(usbLineStatus != 0) /* SE0 has ended */ + goto isNotReset; + } + /* RESET condition, called multiple times during reset */ + usbNewDeviceAddr = 0; + usbDeviceAddr = 0; + usbResetStall(); + DBG1(0xff, 0, 0); +isNotReset: + usbHandleResetHook(i); +} + +/* ------------------------------------------------------------------------- */ + +USB_PUBLIC void usbInit(void) +{ +#if USB_INTR_CFG_SET != 0 + USB_INTR_CFG |= USB_INTR_CFG_SET; +#endif +#if USB_INTR_CFG_CLR != 0 + USB_INTR_CFG &= ~(USB_INTR_CFG_CLR); +#endif + USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT); + usbResetDataToggling(); +#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE + usbTxLen1 = USBPID_NAK; +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 + usbTxLen3 = USBPID_NAK; +#endif +#endif +} + +/* ------------------------------------------------------------------------- */ diff --git a/bootloader/usbdrv/usbdrv.h b/bootloader/usbdrv/usbdrv.h new file mode 100644 index 0000000..98c534e --- /dev/null +++ b/bootloader/usbdrv/usbdrv.h @@ -0,0 +1,735 @@ +/* Name: usbdrv.h + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * This Revision: $Id: usbdrv.h 793 2010-07-15 15:58:11Z cs $ + */ + +#ifndef __usbdrv_h_included__ +#define __usbdrv_h_included__ +#include "usbconfig.h" +#include "usbportability.h" + +/* +Hardware Prerequisites: +======================= +USB lines D+ and D- MUST be wired to the same I/O port. We recommend that D+ +triggers the interrupt (best achieved by using INT0 for D+), but it is also +possible to trigger the interrupt from D-. If D- is used, interrupts are also +triggered by SOF packets. D- requires a pull-up of 1.5k to +3.5V (and the +device must be powered at 3.5V) to identify as low-speed USB device. A +pull-down or pull-up of 1M SHOULD be connected from D+ to +3.5V to prevent +interference when no USB master is connected. If you use Zener diodes to limit +the voltage on D+ and D-, you MUST use a pull-down resistor, not a pull-up. +We use D+ as interrupt source and not D- because it does not trigger on +keep-alive and RESET states. If you want to count keep-alive events with +USB_COUNT_SOF, you MUST use D- as an interrupt source. + +As a compile time option, the 1.5k pull-up resistor on D- can be made +switchable to allow the device to disconnect at will. See the definition of +usbDeviceConnect() and usbDeviceDisconnect() further down in this file. + +Please adapt the values in usbconfig.h according to your hardware! + +The device MUST be clocked at exactly 12 MHz, 15 MHz, 16 MHz or 20 MHz +or at 12.8 MHz resp. 16.5 MHz +/- 1%. See usbconfig-prototype.h for details. + + +Limitations: +============ +Robustness with respect to communication errors: +The driver assumes error-free communication. It DOES check for errors in +the PID, but does NOT check bit stuffing errors, SE0 in middle of a byte, +token CRC (5 bit) and data CRC (16 bit). CRC checks can not be performed due +to timing constraints: We must start sending a reply within 7 bit times. +Bit stuffing and misplaced SE0 would have to be checked in real-time, but CPU +performance does not permit that. The driver does not check Data0/Data1 +toggling, but application software can implement the check. + +Input characteristics: +Since no differential receiver circuit is used, electrical interference +robustness may suffer. The driver samples only one of the data lines with +an ordinary I/O pin's input characteristics. However, since this is only a +low speed USB implementation and the specification allows for 8 times the +bit rate over the same hardware, we should be on the safe side. Even the spec +requires detection of asymmetric states at high bit rate for SE0 detection. + +Number of endpoints: +The driver supports the following endpoints: + +- Endpoint 0, the default control endpoint. +- Any number of interrupt- or bulk-out endpoints. The data is sent to + usbFunctionWriteOut() and USB_CFG_IMPLEMENT_FN_WRITEOUT must be defined + to 1 to activate this feature. The endpoint number can be found in the + global variable 'usbRxToken'. +- One default interrupt- or bulk-in endpoint. This endpoint is used for + interrupt- or bulk-in transfers which are not handled by any other endpoint. + You must define USB_CFG_HAVE_INTRIN_ENDPOINT in order to activate this + feature and call usbSetInterrupt() to send interrupt/bulk data. +- One additional interrupt- or bulk-in endpoint. This was endpoint 3 in + previous versions of this driver but can now be configured to any endpoint + number. You must define USB_CFG_HAVE_INTRIN_ENDPOINT3 in order to activate + this feature and call usbSetInterrupt3() to send interrupt/bulk data. The + endpoint number can be set with USB_CFG_EP3_NUMBER. + +Please note that the USB standard forbids bulk endpoints for low speed devices! +Most operating systems allow them anyway, but the AVR will spend 90% of the CPU +time in the USB interrupt polling for bulk data. + +Maximum data payload: +Data payload of control in and out transfers may be up to 254 bytes. In order +to accept payload data of out transfers, you need to implement +'usbFunctionWrite()'. + +USB Suspend Mode supply current: +The USB standard limits power consumption to 500uA when the bus is in suspend +mode. This is not a problem for self-powered devices since they don't need +bus power anyway. Bus-powered devices can achieve this only by putting the +CPU in sleep mode. The driver does not implement suspend handling by itself. +However, the application may implement activity monitoring and wakeup from +sleep. The host sends regular SE0 states on the bus to keep it active. These +SE0 states can be detected by using D- as the interrupt source. Define +USB_COUNT_SOF to 1 and use the global variable usbSofCount to check for bus +activity. + +Operation without an USB master: +The driver behaves neutral without connection to an USB master if D- reads +as 1. To avoid spurious interrupts, we recommend a high impedance (e.g. 1M) +pull-down or pull-up resistor on D+ (interrupt). If Zener diodes are used, +use a pull-down. If D- becomes statically 0, the driver may block in the +interrupt routine. + +Interrupt latency: +The application must ensure that the USB interrupt is not disabled for more +than 25 cycles (this is for 12 MHz, faster clocks allow longer latency). +This implies that all interrupt routines must either have the "ISR_NOBLOCK" +attribute set (see "avr/interrupt.h") or be written in assembler with "sei" +as the first instruction. + +Maximum interrupt duration / CPU cycle consumption: +The driver handles all USB communication during the interrupt service +routine. The routine will not return before an entire USB message is received +and the reply is sent. This may be up to ca. 1200 cycles @ 12 MHz (= 100us) if +the host conforms to the standard. The driver will consume CPU cycles for all +USB messages, even if they address another (low-speed) device on the same bus. + +*/ + +/* ------------------------------------------------------------------------- */ +/* --------------------------- Module Interface ---------------------------- */ +/* ------------------------------------------------------------------------- */ + +#define USBDRV_VERSION 20100715 +/* This define uniquely identifies a driver version. It is a decimal number + * constructed from the driver's release date in the form YYYYMMDD. If the + * driver's behavior or interface changes, you can use this constant to + * distinguish versions. If it is not defined, the driver's release date is + * older than 2006-01-25. + */ + + +#ifndef USB_PUBLIC +#define USB_PUBLIC +#endif +/* USB_PUBLIC is used as declaration attribute for all functions exported by + * the USB driver. The default is no attribute (see above). You may define it + * to static either in usbconfig.h or from the command line if you include + * usbdrv.c instead of linking against it. Including the C module of the driver + * directly in your code saves a couple of bytes in flash memory. + */ + +#ifndef __ASSEMBLER__ +#ifndef uchar +#define uchar unsigned char +#endif +#ifndef schar +#define schar signed char +#endif +/* shortcuts for well defined 8 bit integer types */ + +#if USB_CFG_LONG_TRANSFERS /* if more than 254 bytes transfer size required */ +# define usbMsgLen_t unsigned +#else +# define usbMsgLen_t uchar +#endif +/* usbMsgLen_t is the data type used for transfer lengths. By default, it is + * defined to uchar, allowing a maximum of 254 bytes (255 is reserved for + * USB_NO_MSG below). If the usbconfig.h defines USB_CFG_LONG_TRANSFERS to 1, + * a 16 bit data type is used, allowing up to 16384 bytes (the rest is used + * for flags in the descriptor configuration). + */ +#define USB_NO_MSG ((usbMsgLen_t)-1) /* constant meaning "no message" */ + +struct usbRequest; /* forward declaration */ + +USB_PUBLIC void usbInit(void); +/* This function must be called before interrupts are enabled and the main + * loop is entered. We exepct that the PORT and DDR bits for D+ and D- have + * not been changed from their default status (which is 0). If you have changed + * them, set both back to 0 (configure them as input with no internal pull-up). + */ +USB_PUBLIC void usbPoll(void); +/* This function must be called at regular intervals from the main loop. + * Maximum delay between calls is somewhat less than 50ms (USB timeout for + * accepting a Setup message). Otherwise the device will not be recognized. + * Please note that debug outputs through the UART take ~ 0.5ms per byte + * at 19200 bps. + */ +extern uchar *usbMsgPtr; +/* This variable may be used to pass transmit data to the driver from the + * implementation of usbFunctionWrite(). It is also used internally by the + * driver for standard control requests. + */ +USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]); +/* This function is called when the driver receives a SETUP transaction from + * the host which is not answered by the driver itself (in practice: class and + * vendor requests). All control transfers start with a SETUP transaction where + * the host communicates the parameters of the following (optional) data + * transfer. The SETUP data is available in the 'data' parameter which can + * (and should) be casted to 'usbRequest_t *' for a more user-friendly access + * to parameters. + * + * If the SETUP indicates a control-in transfer, you should provide the + * requested data to the driver. There are two ways to transfer this data: + * (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data + * block and return the length of the data in 'usbFunctionSetup()'. The driver + * will handle the rest. Or (2) return USB_NO_MSG in 'usbFunctionSetup()'. The + * driver will then call 'usbFunctionRead()' when data is needed. See the + * documentation for usbFunctionRead() for details. + * + * If the SETUP indicates a control-out transfer, the only way to receive the + * data from the host is through the 'usbFunctionWrite()' call. If you + * implement this function, you must return USB_NO_MSG in 'usbFunctionSetup()' + * to indicate that 'usbFunctionWrite()' should be used. See the documentation + * of this function for more information. If you just want to ignore the data + * sent by the host, return 0 in 'usbFunctionSetup()'. + * + * Note that calls to the functions usbFunctionRead() and usbFunctionWrite() + * are only done if enabled by the configuration in usbconfig.h. + */ +USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq); +/* You need to implement this function ONLY if you provide USB descriptors at + * runtime (which is an expert feature). It is very similar to + * usbFunctionSetup() above, but it is called only to request USB descriptor + * data. See the documentation of usbFunctionSetup() above for more info. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT +USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len); +/* This function sets the message which will be sent during the next interrupt + * IN transfer. The message is copied to an internal buffer and must not exceed + * a length of 8 bytes. The message may be 0 bytes long just to indicate the + * interrupt status to the host. + * If you need to transfer more bytes, use a control read after the interrupt. + */ +#define usbInterruptIsReady() (usbTxLen1 & 0x10) +/* This macro indicates whether the last interrupt message has already been + * sent. If you set a new interrupt message before the old was sent, the + * message already buffered will be lost. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len); +#define usbInterruptIsReady3() (usbTxLen3 & 0x10) +/* Same as above for endpoint 3 */ +#endif +#endif /* USB_CFG_HAVE_INTRIN_ENDPOINT */ +#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* simplified interface for backward compatibility */ +#define usbHidReportDescriptor usbDescriptorHidReport +/* should be declared as: PROGMEM char usbHidReportDescriptor[]; */ +/* If you implement an HID device, you need to provide a report descriptor. + * The HID report descriptor syntax is a bit complex. If you understand how + * report descriptors are constructed, we recommend that you use the HID + * Descriptor Tool from usb.org, see http://www.usb.org/developers/hidpage/. + * Otherwise you should probably start with a working example. + */ +#endif /* USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH */ +#if USB_CFG_IMPLEMENT_FN_WRITE +USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len); +/* This function is called by the driver to provide a control transfer's + * payload data (control-out). It is called in chunks of up to 8 bytes. The + * total count provided in the current control transfer can be obtained from + * the 'length' property in the setup data. If an error occurred during + * processing, return 0xff (== -1). The driver will answer the entire transfer + * with a STALL token in this case. If you have received the entire payload + * successfully, return 1. If you expect more data, return 0. If you don't + * know whether the host will send more data (you should know, the total is + * provided in the usbFunctionSetup() call!), return 1. + * NOTE: If you return 0xff for STALL, 'usbFunctionWrite()' may still be called + * for the remaining data. You must continue to return 0xff for STALL in these + * calls. + * In order to get usbFunctionWrite() called, define USB_CFG_IMPLEMENT_FN_WRITE + * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. + */ +#endif /* USB_CFG_IMPLEMENT_FN_WRITE */ +#if USB_CFG_IMPLEMENT_FN_READ +USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len); +/* This function is called by the driver to ask the application for a control + * transfer's payload data (control-in). It is called in chunks of up to 8 + * bytes each. You should copy the data to the location given by 'data' and + * return the actual number of bytes copied. If you return less than requested, + * the control-in transfer is terminated. If you return 0xff, the driver aborts + * the transfer with a STALL token. + * In order to get usbFunctionRead() called, define USB_CFG_IMPLEMENT_FN_READ + * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. + */ +#endif /* USB_CFG_IMPLEMENT_FN_READ */ + +extern uchar usbRxToken; /* may be used in usbFunctionWriteOut() below */ +#if USB_CFG_IMPLEMENT_FN_WRITEOUT +USB_PUBLIC void usbFunctionWriteOut(uchar *data, uchar len); +/* This function is called by the driver when data is received on an interrupt- + * or bulk-out endpoint. The endpoint number can be found in the global + * variable usbRxToken. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT to 1 in + * usbconfig.h to get this function called. + */ +#endif /* USB_CFG_IMPLEMENT_FN_WRITEOUT */ +#ifdef USB_CFG_PULLUP_IOPORTNAME +#define usbDeviceConnect() ((USB_PULLUP_DDR |= (1<device, 1=device->host + * t ..... type: 0=standard, 1=class, 2=vendor, 3=reserved + * r ..... recipient: 0=device, 1=interface, 2=endpoint, 3=other + */ + +/* USB setup recipient values */ +#define USBRQ_RCPT_MASK 0x1f +#define USBRQ_RCPT_DEVICE 0 +#define USBRQ_RCPT_INTERFACE 1 +#define USBRQ_RCPT_ENDPOINT 2 + +/* USB request type values */ +#define USBRQ_TYPE_MASK 0x60 +#define USBRQ_TYPE_STANDARD (0<<5) +#define USBRQ_TYPE_CLASS (1<<5) +#define USBRQ_TYPE_VENDOR (2<<5) + +/* USB direction values: */ +#define USBRQ_DIR_MASK 0x80 +#define USBRQ_DIR_HOST_TO_DEVICE (0<<7) +#define USBRQ_DIR_DEVICE_TO_HOST (1<<7) + +/* USB Standard Requests */ +#define USBRQ_GET_STATUS 0 +#define USBRQ_CLEAR_FEATURE 1 +#define USBRQ_SET_FEATURE 3 +#define USBRQ_SET_ADDRESS 5 +#define USBRQ_GET_DESCRIPTOR 6 +#define USBRQ_SET_DESCRIPTOR 7 +#define USBRQ_GET_CONFIGURATION 8 +#define USBRQ_SET_CONFIGURATION 9 +#define USBRQ_GET_INTERFACE 10 +#define USBRQ_SET_INTERFACE 11 +#define USBRQ_SYNCH_FRAME 12 + +/* USB descriptor constants */ +#define USBDESCR_DEVICE 1 +#define USBDESCR_CONFIG 2 +#define USBDESCR_STRING 3 +#define USBDESCR_INTERFACE 4 +#define USBDESCR_ENDPOINT 5 +#define USBDESCR_HID 0x21 +#define USBDESCR_HID_REPORT 0x22 +#define USBDESCR_HID_PHYS 0x23 + +//#define USBATTR_BUSPOWER 0x80 // USB 1.1 does not define this value any more +#define USBATTR_SELFPOWER 0x40 +#define USBATTR_REMOTEWAKE 0x20 + +/* USB HID Requests */ +#define USBRQ_HID_GET_REPORT 0x01 +#define USBRQ_HID_GET_IDLE 0x02 +#define USBRQ_HID_GET_PROTOCOL 0x03 +#define USBRQ_HID_SET_REPORT 0x09 +#define USBRQ_HID_SET_IDLE 0x0a +#define USBRQ_HID_SET_PROTOCOL 0x0b + +/* ------------------------------------------------------------------------- */ + +#endif /* __usbdrv_h_included__ */ diff --git a/bootloader/usbdrv/usbdrvasm.S b/bootloader/usbdrv/usbdrvasm.S new file mode 100644 index 0000000..45fcf18 --- /dev/null +++ b/bootloader/usbdrv/usbdrvasm.S @@ -0,0 +1,393 @@ +/* Name: usbdrvasm.S + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2007-06-13 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * Revision: $Id: usbdrvasm.S 785 2010-05-30 17:57:07Z cs $ + */ + +/* +General Description: +This module is the assembler part of the USB driver. This file contains +general code (preprocessor acrobatics and CRC computation) and then includes +the file appropriate for the given clock rate. +*/ + +#define __SFR_OFFSET 0 /* used by avr-libc's register definitions */ +#include "usbportability.h" +#include "usbdrv.h" /* for common defs */ + +/* register names */ +#define x1 r16 +#define x2 r17 +#define shift r18 +#define cnt r19 +#define x3 r20 +#define x4 r21 +#define x5 r22 +#define bitcnt x5 +#define phase x4 +#define leap x4 + +/* Some assembler dependent definitions and declarations: */ + +#ifdef __IAR_SYSTEMS_ASM__ + extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset + extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen + extern usbTxBuf, usbTxStatus1, usbTxStatus3 +# if USB_COUNT_SOF + extern usbSofCount +# endif + public usbCrc16 + public usbCrc16Append + + COMMON INTVEC +# ifndef USB_INTR_VECTOR + ORG INT0_vect +# else /* USB_INTR_VECTOR */ + ORG USB_INTR_VECTOR +# undef USB_INTR_VECTOR +# endif /* USB_INTR_VECTOR */ +# define USB_INTR_VECTOR usbInterruptHandler + rjmp USB_INTR_VECTOR + RSEG CODE + +#else /* __IAR_SYSTEMS_ASM__ */ + +# ifndef USB_INTR_VECTOR /* default to hardware interrupt INT0 */ +# ifdef INT0_vect +# define USB_INTR_VECTOR INT0_vect // this is the "new" define for the vector +# else +# define USB_INTR_VECTOR SIG_INTERRUPT0 // this is the "old" vector +# endif +# endif + .text + .global USB_INTR_VECTOR + .type USB_INTR_VECTOR, @function + .global usbCrc16 + .global usbCrc16Append +#endif /* __IAR_SYSTEMS_ASM__ */ + + +#if USB_INTR_PENDING < 0x40 /* This is an I/O address, use in and out */ +# define USB_LOAD_PENDING(reg) in reg, USB_INTR_PENDING +# define USB_STORE_PENDING(reg) out USB_INTR_PENDING, reg +#else /* It's a memory address, use lds and sts */ +# define USB_LOAD_PENDING(reg) lds reg, USB_INTR_PENDING +# define USB_STORE_PENDING(reg) sts USB_INTR_PENDING, reg +#endif + +#define usbTxLen1 usbTxStatus1 +#define usbTxBuf1 (usbTxStatus1 + 1) +#define usbTxLen3 usbTxStatus3 +#define usbTxBuf3 (usbTxStatus3 + 1) + + +;---------------------------------------------------------------------------- +; Utility functions +;---------------------------------------------------------------------------- + +#ifdef __IAR_SYSTEMS_ASM__ +/* Register assignments for usbCrc16 on IAR cc */ +/* Calling conventions on IAR: + * First parameter passed in r16/r17, second in r18/r19 and so on. + * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) + * Result is passed in r16/r17 + * In case of the "tiny" memory model, pointers are only 8 bit with no + * padding. We therefore pass argument 1 as "16 bit unsigned". + */ +RTMODEL "__rt_version", "3" +/* The line above will generate an error if cc calling conventions change. + * The value "3" above is valid for IAR 4.10B/W32 + */ +# define argLen r18 /* argument 2 */ +# define argPtrL r16 /* argument 1 */ +# define argPtrH r17 /* argument 1 */ + +# define resCrcL r16 /* result */ +# define resCrcH r17 /* result */ + +# define ptrL ZL +# define ptrH ZH +# define ptr Z +# define byte r22 +# define bitCnt r19 +# define polyL r20 +# define polyH r21 +# define scratch r23 + +#else /* __IAR_SYSTEMS_ASM__ */ +/* Register assignments for usbCrc16 on gcc */ +/* Calling conventions on gcc: + * First parameter passed in r24/r25, second in r22/23 and so on. + * Callee must preserve r1-r17, r28/r29 + * Result is passed in r24/r25 + */ +# define argLen r22 /* argument 2 */ +# define argPtrL r24 /* argument 1 */ +# define argPtrH r25 /* argument 1 */ + +# define resCrcL r24 /* result */ +# define resCrcH r25 /* result */ + +# define ptrL XL +# define ptrH XH +# define ptr x +# define byte r18 +# define bitCnt r19 +# define polyL r20 +# define polyH r21 +# define scratch r23 + +#endif + +#if USB_USE_FAST_CRC + +; This implementation is faster, but has bigger code size +; Thanks to Slawomir Fras (BoskiDialer) for this code! +; It implements the following C pseudo-code: +; unsigned table(unsigned char x) +; { +; unsigned value; +; +; value = (unsigned)x << 6; +; value ^= (unsigned)x << 7; +; if(parity(x)) +; value ^= 0xc001; +; return value; +; } +; unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen) +; { +; unsigned crc = 0xffff; +; +; while(argLen--) +; crc = table(lo8(crc) ^ *argPtr++) ^ hi8(crc); +; return ~crc; +; } + +; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen); +; argPtr r24+25 / r16+r17 +; argLen r22 / r18 +; temp variables: +; byte r18 / r22 +; scratch r23 +; resCrc r24+r25 / r16+r17 +; ptr X / Z +usbCrc16: + mov ptrL, argPtrL + mov ptrH, argPtrH + ldi resCrcL, 0xFF + ldi resCrcH, 0xFF + rjmp usbCrc16LoopTest +usbCrc16ByteLoop: + ld byte, ptr+ + eor resCrcL, byte ; resCrcL is now 'x' in table() + mov byte, resCrcL ; compute parity of 'x' + swap byte + eor byte, resCrcL + mov scratch, byte + lsr byte + lsr byte + eor byte, scratch + inc byte + lsr byte + andi byte, 1 ; byte is now parity(x) + mov scratch, resCrcL + mov resCrcL, resCrcH + eor resCrcL, byte ; low byte of if(parity(x)) value ^= 0xc001; + neg byte + andi byte, 0xc0 + mov resCrcH, byte ; high byte of if(parity(x)) value ^= 0xc001; + clr byte + lsr scratch + ror byte + eor resCrcH, scratch + eor resCrcL, byte + lsr scratch + ror byte + eor resCrcH, scratch + eor resCrcL, byte +usbCrc16LoopTest: + subi argLen, 1 + brsh usbCrc16ByteLoop + com resCrcL + com resCrcH + ret + +#else /* USB_USE_FAST_CRC */ + +; This implementation is slower, but has less code size +; +; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen); +; argPtr r24+25 / r16+r17 +; argLen r22 / r18 +; temp variables: +; byte r18 / r22 +; bitCnt r19 +; poly r20+r21 +; scratch r23 +; resCrc r24+r25 / r16+r17 +; ptr X / Z +usbCrc16: + mov ptrL, argPtrL + mov ptrH, argPtrH + ldi resCrcL, 0 + ldi resCrcH, 0 + ldi polyL, lo8(0xa001) + ldi polyH, hi8(0xa001) + com argLen ; argLen = -argLen - 1: modified loop to ensure that carry is set + ldi bitCnt, 0 ; loop counter with starnd condition = end condition + rjmp usbCrcLoopEntry +usbCrcByteLoop: + ld byte, ptr+ + eor resCrcL, byte +usbCrcBitLoop: + ror resCrcH ; carry is always set here (see brcs jumps to here) + ror resCrcL + brcs usbCrcNoXor + eor resCrcL, polyL + eor resCrcH, polyH +usbCrcNoXor: + subi bitCnt, 224 ; (8 * 224) % 256 = 0; this loop iterates 8 times + brcs usbCrcBitLoop +usbCrcLoopEntry: + subi argLen, -1 + brcs usbCrcByteLoop +usbCrcReady: + ret +; Thanks to Reimar Doeffinger for optimizing this CRC routine! + +#endif /* USB_USE_FAST_CRC */ + +; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len); +usbCrc16Append: + rcall usbCrc16 + st ptr+, resCrcL + st ptr+, resCrcH + ret + +#undef argLen +#undef argPtrL +#undef argPtrH +#undef resCrcL +#undef resCrcH +#undef ptrL +#undef ptrH +#undef ptr +#undef byte +#undef bitCnt +#undef polyL +#undef polyH +#undef scratch + + +#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH +#ifdef __IAR_SYSTEMS_ASM__ +/* Register assignments for usbMeasureFrameLength on IAR cc */ +/* Calling conventions on IAR: + * First parameter passed in r16/r17, second in r18/r19 and so on. + * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) + * Result is passed in r16/r17 + * In case of the "tiny" memory model, pointers are only 8 bit with no + * padding. We therefore pass argument 1 as "16 bit unsigned". + */ +# define resL r16 +# define resH r17 +# define cnt16L r30 +# define cnt16H r31 +# define cntH r18 + +#else /* __IAR_SYSTEMS_ASM__ */ +/* Register assignments for usbMeasureFrameLength on gcc */ +/* Calling conventions on gcc: + * First parameter passed in r24/r25, second in r22/23 and so on. + * Callee must preserve r1-r17, r28/r29 + * Result is passed in r24/r25 + */ +# define resL r24 +# define resH r25 +# define cnt16L r24 +# define cnt16H r25 +# define cntH r26 +#endif +# define cnt16 cnt16L + +; extern unsigned usbMeasurePacketLength(void); +; returns time between two idle strobes in multiples of 7 CPU clocks +.global usbMeasureFrameLength +usbMeasureFrameLength: + ldi cntH, 6 ; wait ~ 10 ms for D- == 0 + clr cnt16L + clr cnt16H +usbMFTime16: + dec cntH + breq usbMFTimeout +usbMFWaitStrobe: ; first wait for D- == 0 (idle strobe) + sbiw cnt16, 1 ;[0] [6] + breq usbMFTime16 ;[2] + sbic USBIN, USBMINUS ;[3] + rjmp usbMFWaitStrobe ;[4] +usbMFWaitIdle: ; then wait until idle again + sbis USBIN, USBMINUS ;1 wait for D- == 1 + rjmp usbMFWaitIdle ;2 + ldi cnt16L, 1 ;1 represents cycles so far + clr cnt16H ;1 +usbMFWaitLoop: + in cntH, USBIN ;[0] [7] + adiw cnt16, 1 ;[1] + breq usbMFTimeout ;[3] + andi cntH, USBMASK ;[4] + brne usbMFWaitLoop ;[5] +usbMFTimeout: +#if resL != cnt16L + mov resL, cnt16L + mov resH, cnt16H +#endif + ret + +#undef resL +#undef resH +#undef cnt16 +#undef cnt16L +#undef cnt16H +#undef cntH + +#endif /* USB_CFG_HAVE_MEASURE_FRAME_LENGTH */ + +;---------------------------------------------------------------------------- +; Now include the clock rate specific code +;---------------------------------------------------------------------------- + +#ifndef USB_CFG_CLOCK_KHZ +# ifdef F_CPU +# define USB_CFG_CLOCK_KHZ (F_CPU/1000) +# else +# error "USB_CFG_CLOCK_KHZ not defined in usbconfig.h and no F_CPU set!" +# endif +#endif + +#if USB_CFG_CHECK_CRC /* separate dispatcher for CRC type modules */ +# if USB_CFG_CLOCK_KHZ == 18000 +# include "usbdrvasm18-crc.inc" +# else +# error "USB_CFG_CLOCK_KHZ is not one of the supported crc-rates!" +# endif +#else /* USB_CFG_CHECK_CRC */ +# if USB_CFG_CLOCK_KHZ == 12000 +# include "usbdrvasm12.inc" +# elif USB_CFG_CLOCK_KHZ == 12800 +# include "usbdrvasm128.inc" +# elif USB_CFG_CLOCK_KHZ == 15000 +# include "usbdrvasm15.inc" +# elif USB_CFG_CLOCK_KHZ == 16000 +# include "usbdrvasm16.inc" +# elif USB_CFG_CLOCK_KHZ == 16500 +# include "usbdrvasm165.inc" +# elif USB_CFG_CLOCK_KHZ == 20000 +# include "usbdrvasm20.inc" +# else +# error "USB_CFG_CLOCK_KHZ is not one of the supported non-crc-rates!" +# endif +#endif /* USB_CFG_CHECK_CRC */ diff --git a/bootloader/usbdrv/usbdrvasm.asm b/bootloader/usbdrv/usbdrvasm.asm new file mode 100644 index 0000000..9cc4e4d --- /dev/null +++ b/bootloader/usbdrv/usbdrvasm.asm @@ -0,0 +1,21 @@ +/* Name: usbdrvasm.asm + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2006-03-01 + * Tabsize: 4 + * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * This Revision: $Id$ + */ + +/* +General Description: +The IAR compiler/assembler system prefers assembler files with file extension +".asm". We simply provide this file as an alias for usbdrvasm.S. + +Thanks to Oleg Semyonov for his help with the IAR tools port! +*/ + +#include "usbdrvasm.S" + +end diff --git a/bootloader/usbdrv/usbdrvasm.o b/bootloader/usbdrv/usbdrvasm.o new file mode 100644 index 0000000..e9f98b6 Binary files /dev/null and b/bootloader/usbdrv/usbdrvasm.o differ diff --git a/bootloader/usbdrv/usbdrvasm12.inc b/bootloader/usbdrv/usbdrvasm12.inc new file mode 100644 index 0000000..c116758 --- /dev/null +++ b/bootloader/usbdrv/usbdrvasm12.inc @@ -0,0 +1,393 @@ +/* Name: usbdrvasm12.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * This Revision: $Id: usbdrvasm12.inc 740 2009-04-13 18:23:31Z cs $ + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 12 MHz version of the asssembler part of the USB driver. It +requires a 12 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! + + +Timing constraints according to spec (in bit times): +timing subject min max CPUcycles +--------------------------------------------------------------------------- +EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2 16 16-128 +EOP of IN to sync pattern of DATA0 (rx, then tx) 2 7.5 16-60 +DATAx (rx) to ACK/NAK/STALL (tx) 2 7.5 16-60 +*/ + +;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! +;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled +;max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable +;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes +;Numbers in brackets are maximum cycles since SOF. +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt + push YL ;2 [35] push only what is necessary to sync with edge ASAP + in YL, SREG ;1 [37] + push YL ;2 [39] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of 1/4 bit which meets the spec. + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push YH ;2 [2] + lds YL, usbInputBufOffset;2 [4] + clr YH ;1 [5] + subi YL, lo8(-(usbRxBuf));1 [6] + sbci YH, hi8(-(usbRxBuf));1 [7] + + sbis USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early] + rjmp haveTwoBitsK ;2 [10] + pop YH ;2 [11] undo the push from before + rjmp waitForK ;2 [13] this was not the end of sync, retry +haveTwoBitsK: +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- + push shift ;2 [16] + push x1 ;2 [12] + push x2 ;2 [14] + + in x1, USBIN ;1 [17] <-- sample bit 0 + ldi shift, 0xff ;1 [18] + bst x1, USBMINUS ;1 [19] + bld shift, 0 ;1 [20] + push x3 ;2 [22] + push cnt ;2 [24] + + in x2, USBIN ;1 [25] <-- sample bit 1 + ser x3 ;1 [26] [inserted init instruction] + eor x1, x2 ;1 [27] + bst x1, USBMINUS ;1 [28] + bld shift, 1 ;1 [29] + ldi cnt, USB_BUFSIZE;1 [30] [inserted init instruction] + rjmp rxbit2 ;2 [32] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +unstuff0: ;1 (branch taken) + andi x3, ~0x01 ;1 [15] + mov x1, x2 ;1 [16] x2 contains last sampled (stuffed) bit + in x2, USBIN ;1 [17] <-- sample bit 1 again + ori shift, 0x01 ;1 [18] + rjmp didUnstuff0 ;2 [20] + +unstuff1: ;1 (branch taken) + mov x2, x1 ;1 [21] x1 contains last sampled (stuffed) bit + andi x3, ~0x02 ;1 [22] + ori shift, 0x02 ;1 [23] + nop ;1 [24] + in x1, USBIN ;1 [25] <-- sample bit 2 again + rjmp didUnstuff1 ;2 [27] + +unstuff2: ;1 (branch taken) + andi x3, ~0x04 ;1 [29] + ori shift, 0x04 ;1 [30] + mov x1, x2 ;1 [31] x2 contains last sampled (stuffed) bit + nop ;1 [32] + in x2, USBIN ;1 [33] <-- sample bit 3 + rjmp didUnstuff2 ;2 [35] + +unstuff3: ;1 (branch taken) + in x2, USBIN ;1 [34] <-- sample stuffed bit 3 [one cycle too late] + andi x3, ~0x08 ;1 [35] + ori shift, 0x08 ;1 [36] + rjmp didUnstuff3 ;2 [38] + +unstuff4: ;1 (branch taken) + andi x3, ~0x10 ;1 [40] + in x1, USBIN ;1 [41] <-- sample stuffed bit 4 + ori shift, 0x10 ;1 [42] + rjmp didUnstuff4 ;2 [44] + +unstuff5: ;1 (branch taken) + andi x3, ~0x20 ;1 [48] + in x2, USBIN ;1 [49] <-- sample stuffed bit 5 + ori shift, 0x20 ;1 [50] + rjmp didUnstuff5 ;2 [52] + +unstuff6: ;1 (branch taken) + andi x3, ~0x40 ;1 [56] + in x1, USBIN ;1 [57] <-- sample stuffed bit 6 + ori shift, 0x40 ;1 [58] + rjmp didUnstuff6 ;2 [60] + +; extra jobs done during bit interval: +; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs] +; bit 1: se0 check +; bit 2: overflow check +; bit 3: recovery from delay [bit 0 tasks took too long] +; bit 4: none +; bit 5: none +; bit 6: none +; bit 7: jump, eor +rxLoop: + eor x3, shift ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others + in x1, USBIN ;1 [1] <-- sample bit 0 + st y+, x3 ;2 [3] store data + ser x3 ;1 [4] + nop ;1 [5] + eor x2, x1 ;1 [6] + bst x2, USBMINUS;1 [7] + bld shift, 0 ;1 [8] + in x2, USBIN ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed) + andi x2, USBMASK ;1 [10] + breq se0 ;1 [11] SE0 check for bit 1 + andi shift, 0xf9 ;1 [12] +didUnstuff0: + breq unstuff0 ;1 [13] + eor x1, x2 ;1 [14] + bst x1, USBMINUS;1 [15] + bld shift, 1 ;1 [16] +rxbit2: + in x1, USBIN ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed) + andi shift, 0xf3 ;1 [18] + breq unstuff1 ;1 [19] do remaining work for bit 1 +didUnstuff1: + subi cnt, 1 ;1 [20] + brcs overflow ;1 [21] loop control + eor x2, x1 ;1 [22] + bst x2, USBMINUS;1 [23] + bld shift, 2 ;1 [24] + in x2, USBIN ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed) + andi shift, 0xe7 ;1 [26] + breq unstuff2 ;1 [27] +didUnstuff2: + eor x1, x2 ;1 [28] + bst x1, USBMINUS;1 [29] + bld shift, 3 ;1 [30] +didUnstuff3: + andi shift, 0xcf ;1 [31] + breq unstuff3 ;1 [32] + in x1, USBIN ;1 [33] <-- sample bit 4 + eor x2, x1 ;1 [34] + bst x2, USBMINUS;1 [35] + bld shift, 4 ;1 [36] +didUnstuff4: + andi shift, 0x9f ;1 [37] + breq unstuff4 ;1 [38] + nop2 ;2 [40] + in x2, USBIN ;1 [41] <-- sample bit 5 + eor x1, x2 ;1 [42] + bst x1, USBMINUS;1 [43] + bld shift, 5 ;1 [44] +didUnstuff5: + andi shift, 0x3f ;1 [45] + breq unstuff5 ;1 [46] + nop2 ;2 [48] + in x1, USBIN ;1 [49] <-- sample bit 6 + eor x2, x1 ;1 [50] + bst x2, USBMINUS;1 [51] + bld shift, 6 ;1 [52] +didUnstuff6: + cpi shift, 0x02 ;1 [53] + brlo unstuff6 ;1 [54] + nop2 ;2 [56] + in x2, USBIN ;1 [57] <-- sample bit 7 + eor x1, x2 ;1 [58] + bst x1, USBMINUS;1 [59] + bld shift, 7 ;1 [60] +didUnstuff7: + cpi shift, 0x04 ;1 [61] + brsh rxLoop ;2 [63] loop control +unstuff7: + andi x3, ~0x80 ;1 [63] + ori shift, 0x80 ;1 [64] + in x2, USBIN ;1 [65] <-- sample stuffed bit 7 + nop ;1 [66] + rjmp didUnstuff7 ;2 [68] + +macro POP_STANDARD ; 12 cycles + pop cnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;---------------------------------------------------------------------------- +; Transmitting data +;---------------------------------------------------------------------------- + +txByteLoop: +txBitloop: +stuffN1Delay: ; [03] + ror shift ;[-5] [11] [59] + brcc doExorN1 ;[-4] [60] + subi x4, 1 ;[-3] + brne commonN1 ;[-2] + lsl shift ;[-1] compensate ror after rjmp stuffDelay + nop ;[00] stuffing consists of just waiting 8 cycles + rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear + +sendNakAndReti: ;0 [-19] 19 cycles until SOP + ldi x3, USBPID_NAK ;1 [-18] + rjmp usbSendX3 ;2 [-16] +sendAckAndReti: ;0 [-19] 19 cycles until SOP + ldi x3, USBPID_ACK ;1 [-18] + rjmp usbSendX3 ;2 [-16] +sendCntAndReti: ;0 [-17] 17 cycles until SOP + mov x3, cnt ;1 [-16] +usbSendX3: ;0 [-16] + ldi YL, 20 ;1 [-15] 'x3' is R20 + ldi YH, 0 ;1 [-14] + ldi cnt, 2 ;1 [-13] +; rjmp usbSendAndReti fallthrough + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 +; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 +; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte +;uses: x1...x2, x4, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x4 = bitstuff cnt] +;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) +usbSendAndReti: + in x2, USBDDR ;[-12] 12 cycles until SOP + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS ;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + out USBDDR, x2 ;[-8] <--- acquire bus + in x1, USBOUT ;[-7] port mirror for tx loop + ldi shift, 0x40 ;[-6] sync byte is first byte sent (we enter loop after ror) + ldi x2, USBMASK ;[-5] + push x4 ;[-4] +doExorN1: + eor x1, x2 ;[-2] [06] [62] + ldi x4, 6 ;[-1] [07] [63] +commonN1: +stuffN2Delay: + out USBOUT, x1 ;[00] [08] [64] <--- set bit + ror shift ;[01] + brcc doExorN2 ;[02] + subi x4, 1 ;[03] + brne commonN2 ;[04] + lsl shift ;[05] compensate ror after rjmp stuffDelay + rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear +doExorN2: + eor x1, x2 ;[04] [12] + ldi x4, 6 ;[05] [13] +commonN2: + nop ;[06] [14] + subi cnt, 171 ;[07] [15] trick: (3 * 171) & 0xff = 1 + out USBOUT, x1 ;[08] [16] <--- set bit + brcs txBitloop ;[09] [25] [41] + +stuff6Delay: + ror shift ;[42] [50] + brcc doExor6 ;[43] + subi x4, 1 ;[44] + brne common6 ;[45] + lsl shift ;[46] compensate ror after rjmp stuffDelay + nop ;[47] stuffing consists of just waiting 8 cycles + rjmp stuff6Delay ;[48] after ror, C bit is reliably clear +doExor6: + eor x1, x2 ;[45] [53] + ldi x4, 6 ;[46] +common6: +stuff7Delay: + ror shift ;[47] [55] + out USBOUT, x1 ;[48] <--- set bit + brcc doExor7 ;[49] + subi x4, 1 ;[50] + brne common7 ;[51] + lsl shift ;[52] compensate ror after rjmp stuffDelay + rjmp stuff7Delay ;[53] after ror, C bit is reliably clear +doExor7: + eor x1, x2 ;[51] [59] + ldi x4, 6 ;[52] +common7: + ld shift, y+ ;[53] + tst cnt ;[55] + out USBOUT, x1 ;[56] <--- set bit + brne txByteLoop ;[57] + +;make SE0: + cbr x1, USBMASK ;[58] prepare SE0 [spec says EOP may be 15 to 18 cycles] + lds x2, usbNewDeviceAddr;[59] + lsl x2 ;[61] we compare with left shifted address + subi YL, 2 + 20 ;[62] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[63] + out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[01] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 12.5625 MHz +max frequency: 69.286 cycles for 8 bit -> 12.99 MHz +nominal frequency: 12.77 MHz ( = sqrt(min * max)) + +sampling positions: (next even number in range [+/- 0.5]) +cycle index range: 0 ... 66 +bits: +.5, 8.875, 17.25, 25.625, 34, 42.375, 50.75, 59.125 +[0/1], [9], [17], [25/+26], [34], [+42/43], [51], [59] + +bit number: 0 1 2 3 4 5 6 7 +spare cycles 1 2 1 2 1 1 1 0 + +operations to perform: duration cycle + ---------------- + eor fix, shift 1 -> 00 + andi phase, USBMASK 1 -> 08 + breq se0 1 -> 16 (moved to 11) + st y+, data 2 -> 24, 25 + mov data, fix 1 -> 33 + ser data 1 -> 41 + subi cnt, 1 1 -> 49 + brcs overflow 1 -> 50 + +layout of samples and operations: +[##] = sample bit +<##> = sample phase +*##* = operation + +0: *00* [01] 02 03 04 <05> 06 07 +1: *08* [09] 10 11 12 <13> 14 15 *16* +2: [17] 18 19 20 <21> 22 23 +3: *24* *25* [26] 27 28 29 <30> 31 32 +4: *33* [34] 35 36 37 <38> 39 40 +5: *41* [42] 43 44 45 <46> 47 48 +6: *49* *50* [51] 52 53 54 <55> 56 57 58 +7: [59] 60 61 62 <63> 64 65 66 +*****************************************************************************/ + +/* we prefer positive expressions (do if condition) instead of negative + * (skip if condition), therefore use defines for skip instructions: + */ +#define ifioclr sbis +#define ifioset sbic +#define ifrclr sbrs +#define ifrset sbrc + +/* The registers "fix" and "data" swap their meaning during the loop. Use + * defines to keep their name constant. + */ +#define fix x2 +#define data x1 +#undef phase /* phase has a default definition to x4 */ +#define phase x3 + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt, r0 + push YL ;2 push only what is necessary to sync with edge ASAP + in YL, SREG ;1 + push YL ;2 +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of 1/4 bit which meets the spec. + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS ;[0] + rjmp foundK ;[1] +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError + +foundK: +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push YH ;[2] + lds YL, usbInputBufOffset;[4] + clr YH ;[6] + subi YL, lo8(-(usbRxBuf));[7] + sbci YH, hi8(-(usbRxBuf));[8] + + sbis USBIN, USBMINUS ;[9] we want two bits K [we want to sample at 8 + 4 - 1.5 = 10.5] + rjmp haveTwoBitsK ;[10] + pop YH ;[11] undo the push from before + rjmp waitForK ;[13] this was not the end of sync, retry +haveTwoBitsK: +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +#define fix x2 +#define data x1 + + push shift ;[12] + push x1 ;[14] + push x2 ;[16] + ldi shift, 0x80 ;[18] prevent bit-unstuffing but init low bits to 0 + ifioset USBIN, USBMINUS ;[19] [01] <--- bit 0 [10.5 + 8 = 18.5] + ori shift, 1<<0 ;[02] + push x3 ;[03] + push cnt ;[05] + push r0 ;[07] + ifioset USBIN, USBMINUS ;[09] <--- bit 1 + ori shift, 1<<1 ;[10] + ser fix ;[11] + ldi cnt, USB_BUFSIZE ;[12] + mov data, shift ;[13] + lsl shift ;[14] + nop2 ;[15] + ifioset USBIN, USBMINUS ;[17] <--- bit 2 + ori data, 3<<2 ;[18] store in bit 2 AND bit 3 + eor shift, data ;[19] do nrzi decoding + andi data, 1<<3 ;[20] + in phase, USBIN ;[21] <- phase + brne jumpToEntryAfterSet ;[22] if USBMINS at bit 3 was 1 + nop ;[23] + rjmp entryAfterClr ;[24] +jumpToEntryAfterSet: + rjmp entryAfterSet ;[24] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +#undef fix +#define fix x1 +#undef data +#define data x2 + +bit7IsSet: + ifrclr phase, USBMINUS ;[62] check phase only if D- changed + lpm ;[63] + in phase, USBIN ;[64] <- phase (one cycle too late) + ori shift, 1 << 7 ;[65] + nop ;[66] +;;;;rjmp bit0AfterSet ; -> [00] == [67] moved block up to save jump +bit0AfterSet: + eor fix, shift ;[00] +#undef fix +#define fix x2 +#undef data +#define data x1 /* we now have result in data, fix is reset to 0xff */ + ifioclr USBIN, USBMINUS ;[01] <--- sample 0 + rjmp bit0IsClr ;[02] + andi shift, ~(7 << 0) ;[03] + breq unstuff0s ;[04] + in phase, USBIN ;[05] <- phase + rjmp bit1AfterSet ;[06] +unstuff0s: + in phase, USBIN ;[06] <- phase (one cycle too late) + andi fix, ~(1 << 0) ;[07] + ifioclr USBIN, USBMINUS ;[00] + ifioset USBIN, USBPLUS ;[01] + rjmp bit0IsClr ;[02] executed if first expr false or second true +se0AndStore: ; executed only if both bits 0 + st y+, x1 ;[15/17] cycles after start of byte + rjmp se0 ;[17/19] + +bit0IsClr: + ifrset phase, USBMINUS ;[04] check phase only if D- changed + lpm ;[05] + in phase, USBIN ;[06] <- phase (one cycle too late) + ori shift, 1 << 0 ;[07] +bit1AfterClr: + andi phase, USBMASK ;[08] + ifioset USBIN, USBMINUS ;[09] <--- sample 1 + rjmp bit1IsSet ;[10] + breq se0AndStore ;[11] if D- was 0 in bits 0 AND 1 and D+ was 0 in between, we have SE0 + andi shift, ~(7 << 1) ;[12] + in phase, USBIN ;[13] <- phase + breq unstuff1c ;[14] + rjmp bit2AfterClr ;[15] +unstuff1c: + andi fix, ~(1 << 1) ;[16] + nop2 ;[08] + nop2 ;[10] +bit1IsSet: + ifrclr phase, USBMINUS ;[12] check phase only if D- changed + lpm ;[13] + in phase, USBIN ;[14] <- phase (one cycle too late) + ori shift, 1 << 1 ;[15] + nop ;[16] +bit2AfterSet: + ifioclr USBIN, USBMINUS ;[17] <--- sample 2 + rjmp bit2IsClr ;[18] + andi shift, ~(7 << 2) ;[19] + breq unstuff2s ;[20] + in phase, USBIN ;[21] <- phase + rjmp bit3AfterSet ;[22] +unstuff2s: + in phase, USBIN ;[22] <- phase (one cycle too late) + andi fix, ~(1 << 2) ;[23] + nop2 ;[16] + nop2 ;[18] +bit2IsClr: + ifrset phase, USBMINUS ;[20] check phase only if D- changed + lpm ;[21] + in phase, USBIN ;[22] <- phase (one cycle too late) + ori shift, 1 << 2 ;[23] +bit3AfterClr: + st y+, data ;[24] +entryAfterClr: + ifioset USBIN, USBMINUS ;[26] <--- sample 3 + rjmp bit3IsSet ;[27] + andi shift, ~(7 << 3) ;[28] + breq unstuff3c ;[29] + in phase, USBIN ;[30] <- phase + rjmp bit4AfterClr ;[31] +unstuff3c: + in phase, USBIN ;[31] <- phase (one cycle too late) + andi fix, ~(1 << 3) ;[32] + nop2 ;[25] + nop2 ;[27] +bit3IsSet: + ifrclr phase, USBMINUS ;[29] check phase only if D- changed + lpm ;[30] + in phase, USBIN ;[31] <- phase (one cycle too late) + ori shift, 1 << 3 ;[32] +bit4AfterSet: + mov data, fix ;[33] undo this move by swapping defines +#undef fix +#define fix x1 +#undef data +#define data x2 + ifioclr USBIN, USBMINUS ;[34] <--- sample 4 + rjmp bit4IsClr ;[35] + andi shift, ~(7 << 4) ;[36] + breq unstuff4s ;[37] + in phase, USBIN ;[38] <- phase + rjmp bit5AfterSet ;[39] +unstuff4s: + in phase, USBIN ;[39] <- phase (one cycle too late) + andi fix, ~(1 << 4) ;[40] + nop2 ;[33] + nop2 ;[35] +bit4IsClr: + ifrset phase, USBMINUS ;[37] check phase only if D- changed + lpm ;[38] + in phase, USBIN ;[39] <- phase (one cycle too late) + ori shift, 1 << 4 ;[40] +bit5AfterClr: + ser data ;[41] + ifioset USBIN, USBMINUS ;[42] <--- sample 5 + rjmp bit5IsSet ;[43] + andi shift, ~(7 << 5) ;[44] + breq unstuff5c ;[45] + in phase, USBIN ;[46] <- phase + rjmp bit6AfterClr ;[47] +unstuff5c: + in phase, USBIN ;[47] <- phase (one cycle too late) + andi fix, ~(1 << 5) ;[48] + nop2 ;[41] + nop2 ;[43] +bit5IsSet: + ifrclr phase, USBMINUS ;[45] check phase only if D- changed + lpm ;[46] + in phase, USBIN ;[47] <- phase (one cycle too late) + ori shift, 1 << 5 ;[48] +bit6AfterSet: + subi cnt, 1 ;[49] + brcs jumpToOverflow ;[50] + ifioclr USBIN, USBMINUS ;[51] <--- sample 6 + rjmp bit6IsClr ;[52] + andi shift, ~(3 << 6) ;[53] + cpi shift, 2 ;[54] + in phase, USBIN ;[55] <- phase + brlt unstuff6s ;[56] + rjmp bit7AfterSet ;[57] + +jumpToOverflow: + rjmp overflow + +unstuff6s: + andi fix, ~(1 << 6) ;[50] + lpm ;[51] +bit6IsClr: + ifrset phase, USBMINUS ;[54] check phase only if D- changed + lpm ;[55] + in phase, USBIN ;[56] <- phase (one cycle too late) + ori shift, 1 << 6 ;[57] + nop ;[58] +bit7AfterClr: + ifioset USBIN, USBMINUS ;[59] <--- sample 7 + rjmp bit7IsSet ;[60] + andi shift, ~(1 << 7) ;[61] + cpi shift, 4 ;[62] + in phase, USBIN ;[63] <- phase + brlt unstuff7c ;[64] + rjmp bit0AfterClr ;[65] -> [00] == [67] +unstuff7c: + andi fix, ~(1 << 7) ;[58] + nop ;[59] + rjmp bit7IsSet ;[60] + +bit7IsClr: + ifrset phase, USBMINUS ;[62] check phase only if D- changed + lpm ;[63] + in phase, USBIN ;[64] <- phase (one cycle too late) + ori shift, 1 << 7 ;[65] + nop ;[66] +;;;;rjmp bit0AfterClr ; -> [00] == [67] moved block up to save jump +bit0AfterClr: + eor fix, shift ;[00] +#undef fix +#define fix x2 +#undef data +#define data x1 /* we now have result in data, fix is reset to 0xff */ + ifioset USBIN, USBMINUS ;[01] <--- sample 0 + rjmp bit0IsSet ;[02] + andi shift, ~(7 << 0) ;[03] + breq unstuff0c ;[04] + in phase, USBIN ;[05] <- phase + rjmp bit1AfterClr ;[06] +unstuff0c: + in phase, USBIN ;[06] <- phase (one cycle too late) + andi fix, ~(1 << 0) ;[07] + ifioclr USBIN, USBMINUS ;[00] + ifioset USBIN, USBPLUS ;[01] + rjmp bit0IsSet ;[02] executed if first expr false or second true + rjmp se0AndStore ;[03] executed only if both bits 0 +bit0IsSet: + ifrclr phase, USBMINUS ;[04] check phase only if D- changed + lpm ;[05] + in phase, USBIN ;[06] <- phase (one cycle too late) + ori shift, 1 << 0 ;[07] +bit1AfterSet: + andi shift, ~(7 << 1) ;[08] compensated by "ori shift, 1<<1" if bit1IsClr + ifioclr USBIN, USBMINUS ;[09] <--- sample 1 + rjmp bit1IsClr ;[10] + breq unstuff1s ;[11] + nop2 ;[12] do not check for SE0 if bit 0 was 1 + in phase, USBIN ;[14] <- phase (one cycle too late) + rjmp bit2AfterSet ;[15] +unstuff1s: + in phase, USBIN ;[13] <- phase + andi fix, ~(1 << 1) ;[14] + lpm ;[07] + nop2 ;[10] +bit1IsClr: + ifrset phase, USBMINUS ;[12] check phase only if D- changed + lpm ;[13] + in phase, USBIN ;[14] <- phase (one cycle too late) + ori shift, 1 << 1 ;[15] + nop ;[16] +bit2AfterClr: + ifioset USBIN, USBMINUS ;[17] <--- sample 2 + rjmp bit2IsSet ;[18] + andi shift, ~(7 << 2) ;[19] + breq unstuff2c ;[20] + in phase, USBIN ;[21] <- phase + rjmp bit3AfterClr ;[22] +unstuff2c: + in phase, USBIN ;[22] <- phase (one cycle too late) + andi fix, ~(1 << 2) ;[23] + nop2 ;[16] + nop2 ;[18] +bit2IsSet: + ifrclr phase, USBMINUS ;[20] check phase only if D- changed + lpm ;[21] + in phase, USBIN ;[22] <- phase (one cycle too late) + ori shift, 1 << 2 ;[23] +bit3AfterSet: + st y+, data ;[24] +entryAfterSet: + ifioclr USBIN, USBMINUS ;[26] <--- sample 3 + rjmp bit3IsClr ;[27] + andi shift, ~(7 << 3) ;[28] + breq unstuff3s ;[29] + in phase, USBIN ;[30] <- phase + rjmp bit4AfterSet ;[31] +unstuff3s: + in phase, USBIN ;[31] <- phase (one cycle too late) + andi fix, ~(1 << 3) ;[32] + nop2 ;[25] + nop2 ;[27] +bit3IsClr: + ifrset phase, USBMINUS ;[29] check phase only if D- changed + lpm ;[30] + in phase, USBIN ;[31] <- phase (one cycle too late) + ori shift, 1 << 3 ;[32] +bit4AfterClr: + mov data, fix ;[33] undo this move by swapping defines +#undef fix +#define fix x1 +#undef data +#define data x2 + ifioset USBIN, USBMINUS ;[34] <--- sample 4 + rjmp bit4IsSet ;[35] + andi shift, ~(7 << 4) ;[36] + breq unstuff4c ;[37] + in phase, USBIN ;[38] <- phase + rjmp bit5AfterClr ;[39] +unstuff4c: + in phase, USBIN ;[39] <- phase (one cycle too late) + andi fix, ~(1 << 4) ;[40] + nop2 ;[33] + nop2 ;[35] +bit4IsSet: + ifrclr phase, USBMINUS ;[37] check phase only if D- changed + lpm ;[38] + in phase, USBIN ;[39] <- phase (one cycle too late) + ori shift, 1 << 4 ;[40] +bit5AfterSet: + ser data ;[41] + ifioclr USBIN, USBMINUS ;[42] <--- sample 5 + rjmp bit5IsClr ;[43] + andi shift, ~(7 << 5) ;[44] + breq unstuff5s ;[45] + in phase, USBIN ;[46] <- phase + rjmp bit6AfterSet ;[47] +unstuff5s: + in phase, USBIN ;[47] <- phase (one cycle too late) + andi fix, ~(1 << 5) ;[48] + nop2 ;[41] + nop2 ;[43] +bit5IsClr: + ifrset phase, USBMINUS ;[45] check phase only if D- changed + lpm ;[46] + in phase, USBIN ;[47] <- phase (one cycle too late) + ori shift, 1 << 5 ;[48] +bit6AfterClr: + subi cnt, 1 ;[49] + brcs overflow ;[50] + ifioset USBIN, USBMINUS ;[51] <--- sample 6 + rjmp bit6IsSet ;[52] + andi shift, ~(3 << 6) ;[53] + cpi shift, 2 ;[54] + in phase, USBIN ;[55] <- phase + brlt unstuff6c ;[56] + rjmp bit7AfterClr ;[57] +unstuff6c: + andi fix, ~(1 << 6) ;[50] + lpm ;[51] +bit6IsSet: + ifrclr phase, USBMINUS ;[54] check phase only if D- changed + lpm ;[55] + in phase, USBIN ;[56] <- phase (one cycle too late) + ori shift, 1 << 6 ;[57] +bit7AfterSet: + ifioclr USBIN, USBMINUS ;[59] <--- sample 7 + rjmp bit7IsClr ;[60] + andi shift, ~(1 << 7) ;[61] + cpi shift, 4 ;[62] + in phase, USBIN ;[63] <- phase + brlt unstuff7s ;[64] + rjmp bit0AfterSet ;[65] -> [00] == [67] +unstuff7s: + andi fix, ~(1 << 7) ;[58] + nop ;[59] + rjmp bit7IsClr ;[60] + +macro POP_STANDARD ; 14 cycles + pop r0 + pop cnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;---------------------------------------------------------------------------- +; Transmitting data +;---------------------------------------------------------------------------- + +txByteLoop: +txBitloop: +stuffN1Delay: ; [03] + ror shift ;[-5] [11] [63] + brcc doExorN1 ;[-4] [64] + subi x3, 1 ;[-3] + brne commonN1 ;[-2] + lsl shift ;[-1] compensate ror after rjmp stuffDelay + nop ;[00] stuffing consists of just waiting 8 cycles + rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear + +sendNakAndReti: + ldi cnt, USBPID_NAK ;[-19] + rjmp sendCntAndReti ;[-18] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov r0, cnt ;[-16] + ldi YL, 0 ;[-15] R0 address is 0 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 +; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 +; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte +;uses: x1...x3, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x3 = bitstuff cnt] +;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) +usbSendAndReti: + in x2, USBDDR ;[-10] 10 cycles until SOP + ori x2, USBMASK ;[-9] + sbi USBOUT, USBMINUS ;[-8] prepare idle state; D+ and D- must have been 0 (no pullups) + out USBDDR, x2 ;[-6] <--- acquire bus + in x1, USBOUT ;[-5] port mirror for tx loop + ldi shift, 0x40 ;[-4] sync byte is first byte sent (we enter loop after ror) + ldi x2, USBMASK ;[-3] +doExorN1: + eor x1, x2 ;[-2] [06] [62] + ldi x3, 6 ;[-1] [07] [63] +commonN1: +stuffN2Delay: + out USBOUT, x1 ;[00] [08] [64] <--- set bit + ror shift ;[01] + brcc doExorN2 ;[02] + subi x3, 1 ;[03] + brne commonN2 ;[04] + lsl shift ;[05] compensate ror after rjmp stuffDelay + rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear +doExorN2: + eor x1, x2 ;[04] [12] + ldi x3, 6 ;[05] [13] +commonN2: + nop2 ;[06] [14] + subi cnt, 171 ;[08] [16] trick: (3 * 171) & 0xff = 1 + out USBOUT, x1 ;[09] [17] <--- set bit + brcs txBitloop ;[10] [27] [44] + +stuff6Delay: + ror shift ;[45] [53] + brcc doExor6 ;[46] + subi x3, 1 ;[47] + brne common6 ;[48] + lsl shift ;[49] compensate ror after rjmp stuffDelay + nop ;[50] stuffing consists of just waiting 8 cycles + rjmp stuff6Delay ;[51] after ror, C bit is reliably clear +doExor6: + eor x1, x2 ;[48] [56] + ldi x3, 6 ;[49] +common6: +stuff7Delay: + ror shift ;[50] [58] + out USBOUT, x1 ;[51] <--- set bit + brcc doExor7 ;[52] + subi x3, 1 ;[53] + brne common7 ;[54] + lsl shift ;[55] compensate ror after rjmp stuffDelay + rjmp stuff7Delay ;[56] after ror, C bit is reliably clear +doExor7: + eor x1, x2 ;[54] [62] + ldi x3, 6 ;[55] +common7: + ld shift, y+ ;[56] + nop ;[58] + tst cnt ;[59] + out USBOUT, x1 ;[60] [00]<--- set bit + brne txByteLoop ;[61] [01] +;make SE0: + cbr x1, USBMASK ;[02] prepare SE0 [spec says EOP may be 15 to 18 cycles] + lds x2, usbNewDeviceAddr;[03] + lsl x2 ;[05] we compare with left shifted address + subi YL, 2 + 0 ;[06] Only assign address on data packets, not ACK/NAK in r0 + sbci YH, 0 ;[07] + out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[01] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 0) + echo "$s\n"; + } +} + +function printBit($isAfterSet, $bitNum) +{ + ob_start(); + if($isAfterSet){ +?> + ifioclr USBIN, USBMINUS ;[00] <--- sample + rjmp bit#IsClr ;[01] + andi shift, ~(7 << #) ;[02] + breq unstuff#s ;[03] + in phase, USBIN ;[04] <- phase + rjmp bit@AfterSet ;[05] +unstuff#s: + in phase, USBIN ;[05] <- phase (one cycle too late) + andi fix, ~(1 << #) ;[06] + nop2 ;[-1] + nop2 ;[01] +bit#IsClr: + ifrset phase, USBMINUS ;[03] check phase only if D- changed + lpm ;[04] + in phase, USBIN ;[05] <- phase (one cycle too late) + ori shift, 1 << # ;[06] + + ifioset USBIN, USBMINUS ;[00] <--- sample + rjmp bit#IsSet ;[01] + andi shift, ~(7 << #) ;[02] + breq unstuff#c ;[03] + in phase, USBIN ;[04] <- phase + rjmp bit@AfterClr ;[05] +unstuff#c: + in phase, USBIN ;[05] <- phase (one cycle too late) + andi fix, ~(1 << #) ;[06] + nop2 ;[-1] + nop2 ;[01] +bit#IsSet: + ifrclr phase, USBMINUS ;[03] check phase only if D- changed + lpm ;[04] + in phase, USBIN ;[05] <- phase (one cycle too late) + ori shift, 1 << # ;[06] + +*****************************************************************************/ diff --git a/bootloader/usbdrv/usbdrvasm15.inc b/bootloader/usbdrv/usbdrvasm15.inc new file mode 100644 index 0000000..401b7f8 --- /dev/null +++ b/bootloader/usbdrv/usbdrvasm15.inc @@ -0,0 +1,423 @@ +/* Name: usbdrvasm15.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: contributed by V. Bosch + * Creation Date: 2007-08-06 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * Revision: $Id: usbdrvasm15.inc 740 2009-04-13 18:23:31Z cs $ + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 15 MHz version of the asssembler part of the USB driver. It +requires a 15 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! +*/ + +;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 15 MHz -> 10.0 cycles per bit, 80.0 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + +;---------------------------------------------------------------------------- +; order of registers pushed: +; YL, SREG [sofError] YH, shift, x1, x2, x3, bitcnt, cnt, x4 +;---------------------------------------------------------------------------- +USB_INTR_VECTOR: + push YL ;2 push only what is necessary to sync with edge ASAP + in YL, SREG ;1 + push YL ;2 +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +; +; sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +; sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +;------------------------------------------------------------------------------- +; The following code results in a sampling window of < 1/4 bit +; which meets the spec. +;------------------------------------------------------------------------------- +waitForK: ;- + sbis USBIN, USBMINUS ;1 [00] <-- sample + rjmp foundK ;2 [01] + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +;------------------------------------------------------------------------------ +; {3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for +; center sampling] +; we have 1 bit time for setup purposes, then sample again. +; Numbers in brackets are cycles from center of first sync (double K) +; bit after the instruction +;------------------------------------------------------------------------------ +foundK: ;- [02] + lds YL, usbInputBufOffset;2 [03+04] tx loop + push YH ;2 [05+06] + clr YH ;1 [07] + subi YL, lo8(-(usbRxBuf)) ;1 [08] [rx loop init] + sbci YH, hi8(-(usbRxBuf)) ;1 [09] [rx loop init] + push shift ;2 [10+11] + ser shift ;1 [12] + sbis USBIN, USBMINUS ;1 [-1] [13] <--sample:we want two bits K (sample 1 cycle too early) + rjmp haveTwoBitsK ;2 [00] [14] + pop shift ;2 [15+16] undo the push from before + pop YH ;2 [17+18] undo the push from before + rjmp waitForK ;2 [19+20] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 20 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: ;- [01] + push x1 ;2 [02+03] + push x2 ;2 [04+05] + push x3 ;2 [06+07] + push bitcnt ;2 [08+09] + in x1, USBIN ;1 [00] [10] <-- sample bit 0 + bst x1, USBMINUS ;1 [01] + bld shift, 0 ;1 [02] + push cnt ;2 [03+04] + ldi cnt, USB_BUFSIZE ;1 [05] + push x4 ;2 [06+07] tx loop + rjmp rxLoop ;2 [08] +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +unstuff0: ;- [07] (branch taken) + andi x3, ~0x01 ;1 [08] + mov x1, x2 ;1 [09] x2 contains last sampled (stuffed) bit + in x2, USBIN ;1 [00] [10] <-- sample bit 1 again + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 1 + ori shift, 0x01 ;1 [03] 0b00000001 + nop ;1 [04] + rjmp didUnstuff0 ;2 [05] +;----------------------------------------------------- +unstuff1: ;- [05] (branch taken) + mov x2, x1 ;1 [06] x1 contains last sampled (stuffed) bit + andi x3, ~0x02 ;1 [07] + ori shift, 0x02 ;1 [08] 0b00000010 + nop ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 2 again + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 2 + rjmp didUnstuff1 ;2 [03] +;----------------------------------------------------- +unstuff2: ;- [05] (branch taken) + andi x3, ~0x04 ;1 [06] + ori shift, 0x04 ;1 [07] 0b00000100 + mov x1, x2 ;1 [08] x2 contains last sampled (stuffed) bit + nop ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample bit 3 + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 3 + rjmp didUnstuff2 ;2 [03] +;----------------------------------------------------- +unstuff3: ;- [00] [10] (branch taken) + in x2, USBIN ;1 [01] [11] <-- sample stuffed bit 3 one cycle too late + andi x2, USBMASK ;1 [02] + breq se0Hop ;1 [03] SE0 check for stuffed bit 3 + andi x3, ~0x08 ;1 [04] + ori shift, 0x08 ;1 [05] 0b00001000 + rjmp didUnstuff3 ;2 [06] +;---------------------------------------------------------------------------- +; extra jobs done during bit interval: +; +; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs], +; overflow check, jump to the head of rxLoop +; bit 1: SE0 check +; bit 2: SE0 check, recovery from delay [bit 0 tasks took too long] +; bit 3: SE0 check, recovery from delay [bit 0 tasks took too long] +; bit 4: SE0 check, none +; bit 5: SE0 check, none +; bit 6: SE0 check, none +; bit 7: SE0 check, reconstruct: x3 is 0 at bit locations we changed, 1 at others +;---------------------------------------------------------------------------- +rxLoop: ;- [09] + in x2, USBIN ;1 [00] [10] <-- sample bit 1 (or possibly bit 0 stuffed) + andi x2, USBMASK ;1 [01] + brne SkipSe0Hop ;1 [02] +se0Hop: ;- [02] + rjmp se0 ;2 [03] SE0 check for bit 1 +SkipSe0Hop: ;- [03] + ser x3 ;1 [04] + andi shift, 0xf9 ;1 [05] 0b11111001 + breq unstuff0 ;1 [06] +didUnstuff0: ;- [06] + eor x1, x2 ;1 [07] + bst x1, USBMINUS ;1 [08] + bld shift, 1 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 2 (or possibly bit 1 stuffed) + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 2 + andi shift, 0xf3 ;1 [03] 0b11110011 + breq unstuff1 ;1 [04] do remaining work for bit 1 +didUnstuff1: ;- [04] + eor x2, x1 ;1 [05] + bst x2, USBMINUS ;1 [06] + bld shift, 2 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 3 (or possibly bit 2 stuffed) + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 3 + andi shift, 0xe7 ;1 [03] 0b11100111 + breq unstuff2 ;1 [04] +didUnstuff2: ;- [04] + eor x1, x2 ;1 [05] + bst x1, USBMINUS ;1 [06] + bld shift, 3 ;1 [07] +didUnstuff3: ;- [07] + andi shift, 0xcf ;1 [08] 0b11001111 + breq unstuff3 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 4 + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 4 + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 4 ;1 [05] +didUnstuff4: ;- [05] + andi shift, 0x9f ;1 [06] 0b10011111 + breq unstuff4 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 5 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 5 + eor x1, x2 ;1 [03] + bst x1, USBMINUS ;1 [04] + bld shift, 5 ;1 [05] +didUnstuff5: ;- [05] + andi shift, 0x3f ;1 [06] 0b00111111 + breq unstuff5 ;1 [07] + nop2 ;2 [08+09] + in x1, USBIN ;1 [00] [10] <-- sample bit 6 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 6 + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 6 ;1 [05] +didUnstuff6: ;- [05] + cpi shift, 0x02 ;1 [06] 0b00000010 + brlo unstuff6 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 7 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 7 + eor x1, x2 ;1 [03] + bst x1, USBMINUS ;1 [04] + bld shift, 7 ;1 [05] +didUnstuff7: ;- [05] + cpi shift, 0x04 ;1 [06] 0b00000100 + brlo unstuff7 ;1 [07] + eor x3, shift ;1 [08] reconstruct: x3 is 0 at bit locations we changed, 1 at others + nop ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 0 + st y+, x3 ;2 [01+02] store data + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 0 ;1 [05] + subi cnt, 1 ;1 [06] + brcs overflow ;1 [07] + rjmp rxLoop ;2 [08] +;----------------------------------------------------- +unstuff4: ;- [08] + andi x3, ~0x10 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 4 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 4 + ori shift, 0x10 ;1 [03] + rjmp didUnstuff4 ;2 [04] +;----------------------------------------------------- +unstuff5: ;- [08] + ori shift, 0x20 ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 5 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 5 + andi x3, ~0x20 ;1 [03] + rjmp didUnstuff5 ;2 [04] +;----------------------------------------------------- +unstuff6: ;- [08] + andi x3, ~0x40 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 6 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 6 + ori shift, 0x40 ;1 [03] + rjmp didUnstuff6 ;2 [04] +;----------------------------------------------------- +unstuff7: ;- [08] + andi x3, ~0x80 ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 7 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 7 + ori shift, 0x80 ;1 [03] + rjmp didUnstuff7 ;2 [04] + +macro POP_STANDARD ; 16 cycles + pop x4 + pop cnt + pop bitcnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;--------------------------------------------------------------------------- +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies +;--------------------------------------------------------------------------- +bitstuffN: ;- [04] + eor x1, x4 ;1 [05] + clr x2 ;1 [06] + nop ;1 [07] + rjmp didStuffN ;1 [08] +;--------------------------------------------------------------------------- +bitstuff6: ;- [04] + eor x1, x4 ;1 [05] + clr x2 ;1 [06] + rjmp didStuff6 ;1 [07] +;--------------------------------------------------------------------------- +bitstuff7: ;- [02] + eor x1, x4 ;1 [03] + clr x2 ;1 [06] + nop ;1 [05] + rjmp didStuff7 ;1 [06] +;--------------------------------------------------------------------------- +sendNakAndReti: ;- [-19] + ldi x3, USBPID_NAK ;1 [-18] + rjmp sendX3AndReti ;1 [-17] +;--------------------------------------------------------------------------- +sendAckAndReti: ;- [-17] + ldi cnt, USBPID_ACK ;1 [-16] +sendCntAndReti: ;- [-16] + mov x3, cnt ;1 [-15] +sendX3AndReti: ;- [-15] + ldi YL, 20 ;1 [-14] x3==r20 address is 20 + ldi YH, 0 ;1 [-13] + ldi cnt, 2 ;1 [-12] +; rjmp usbSendAndReti fallthrough +;--------------------------------------------------------------------------- +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +;We need not to match the transfer rate exactly because the spec demands +;only 1.5% precision anyway. +usbSendAndReti: ;- [-13] 13 cycles until SOP + in x2, USBDDR ;1 [-12] + ori x2, USBMASK ;1 [-11] + sbi USBOUT, USBMINUS ;2 [-09-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;1 [-08] port mirror for tx loop + out USBDDR, x2 ;1 [-07] <- acquire bus + ; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;1 [-06] exor mask + ldi shift, 0x80 ;1 [-05] sync byte is first byte sent + ldi bitcnt, 6 ;1 [-04] +txBitLoop: ;- [-04] [06] + sbrs shift, 0 ;1 [-03] [07] + eor x1, x4 ;1 [-02] [08] + ror shift ;1 [-01] [09] +didStuffN: ;- [09] + out USBOUT, x1 ;1 [00] [10] <-- out N + ror x2 ;1 [01] + cpi x2, 0xfc ;1 [02] + brcc bitstuffN ;1 [03] + dec bitcnt ;1 [04] + brne txBitLoop ;1 [05] + sbrs shift, 0 ;1 [06] + eor x1, x4 ;1 [07] + ror shift ;1 [08] +didStuff6: ;- [08] + nop ;1 [09] + out USBOUT, x1 ;1 [00] [10] <-- out 6 + ror x2 ;1 [01] + cpi x2, 0xfc ;1 [02] + brcc bitstuff6 ;1 [03] + sbrs shift, 0 ;1 [04] + eor x1, x4 ;1 [05] + ror shift ;1 [06] + ror x2 ;1 [07] +didStuff7: ;- [07] + ldi bitcnt, 6 ;1 [08] + cpi x2, 0xfc ;1 [09] + out USBOUT, x1 ;1 [00] [10] <-- out 7 + brcc bitstuff7 ;1 [01] + ld shift, y+ ;2 [02+03] + dec cnt ;1 [04] + brne txBitLoop ;1 [05] +makeSE0: + cbr x1, USBMASK ;1 [06] prepare SE0 [spec says EOP may be 19 to 23 cycles] + lds x2, usbNewDeviceAddr;2 [07+08] + lsl x2 ;1 [09] we compare with left shifted address +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + out USBOUT, x1 ;1 [00] [10] <-- out SE0-- from now 2 bits==20 cycl. until bus idle + subi YL, 20 + 2 ;1 [01] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;1 [02] + breq skipAddrAssign ;1 [03] + sts usbDeviceAddr, x2 ;2 [04+05] if not skipped: SE0 is one cycle longer +;---------------------------------------------------------------------------- +;end of usbDeviceAddress transfer +skipAddrAssign: ;- [03/04] + ldi x2, 1< 10.6666666 cycles per bit, 85.333333333 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt + push YL ;[-25] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-23] + push YL ;[-22] + push YH ;[-20] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-15] + rjmp foundK ;[-14] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-12] +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push bitcnt ;[-12] +; [---] ;[-11] + lds YL, usbInputBufOffset;[-10] +; [---] ;[-9] + clr YH ;[-8] + subi YL, lo8(-(usbRxBuf));[-7] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-6] [rx loop init] + push shift ;[-5] +; [---] ;[-4] + ldi bitcnt, 0x55 ;[-3] [rx loop init] + sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) + rjmp haveTwoBitsK ;[-1] + pop shift ;[0] undo the push from before + pop bitcnt ;[2] undo the push from before + rjmp waitForK ;[4] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 21 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[1] + push x2 ;[3] + push x3 ;[5] + ldi shift, 0 ;[7] + ldi x3, 1<<4 ;[8] [rx loop init] first sample is inverse bit, compensate that + push x4 ;[9] == leap + + in x1, USBIN ;[11] <-- sample bit 0 + andi x1, USBMASK ;[12] + bst x1, USBMINUS ;[13] + bld shift, 7 ;[14] + push cnt ;[15] + ldi leap, 0 ;[17] [rx loop init] + ldi cnt, USB_BUFSIZE;[18] [rx loop init] + rjmp rxbit1 ;[19] arrives at [21] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +; duration of unstuffing code should be 10.66666667 cycles. We adjust "leap" +; accordingly to approximate this value in the long run. + +unstuff6: + andi x2, USBMASK ;[03] + ori x3, 1<<6 ;[04] will not be shifted any more + andi shift, ~0x80;[05] + mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6 + subi leap, -1 ;[07] total duration = 11 bits -> subtract 1/3 + rjmp didUnstuff6 ;[08] + +unstuff7: + ori x3, 1<<7 ;[09] will not be shifted any more + in x2, USBIN ;[00] [10] re-sample bit 7 + andi x2, USBMASK ;[01] + andi shift, ~0x80;[02] + subi leap, 2 ;[03] total duration = 10 bits -> add 1/3 + rjmp didUnstuff7 ;[04] + +unstuffEven: + ori x3, 1<<6 ;[09] will be shifted right 6 times for bit 0 + in x1, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x1, USBMASK ;[02] + breq se0 ;[03] + subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 + nop2 ;[05] + rjmp didUnstuffE ;[06] + +unstuffOdd: + ori x3, 1<<5 ;[09] will be shifted right 4 times for bit 1 + in x2, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x2, USBMASK ;[02] + breq se0 ;[03] + subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 + nop2 ;[05] + rjmp didUnstuffO ;[06] + +rxByteLoop: + andi x1, USBMASK ;[03] + eor x2, x1 ;[04] + subi leap, 1 ;[05] + brpl skipLeap ;[06] + subi leap, -3 ;1 one leap cycle every 3rd byte -> 85 + 1/3 cycles per byte + nop ;1 +skipLeap: + subi x2, 1 ;[08] + ror shift ;[09] +didUnstuff6: + cpi shift, 0xfc ;[10] + in x2, USBIN ;[00] [11] <-- sample bit 7 + brcc unstuff6 ;[01] + andi x2, USBMASK ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] +didUnstuff7: + cpi shift, 0xfc ;[06] + brcc unstuff7 ;[07] + eor x3, shift ;[08] reconstruct: x3 is 1 at bit locations we changed, 0 at others + st y+, x3 ;[09] store data +rxBitLoop: + in x1, USBIN ;[00] [11] <-- sample bit 0/2/4 + andi x1, USBMASK ;[01] + eor x2, x1 ;[02] + andi x3, 0x3f ;[03] topmost two bits reserved for 6 and 7 + subi x2, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffEven ;[07] +didUnstuffE: + lsr x3 ;[08] + lsr x3 ;[09] +rxbit1: + in x2, USBIN ;[00] [10] <-- sample bit 1/3/5 + andi x2, USBMASK ;[01] + breq se0 ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffOdd ;[07] +didUnstuffO: + subi bitcnt, 0xab;[08] == addi 0x55, 0x55 = 0x100/3 + brcs rxBitLoop ;[09] + + subi cnt, 1 ;[10] + in x1, USBIN ;[00] [11] <-- sample bit 6 + brcc rxByteLoop ;[01] + rjmp overflow + +macro POP_STANDARD ; 14 cycles + pop cnt + pop x4 + pop x3 + pop x2 + pop x1 + pop shift + pop bitcnt + endm +macro POP_RETI ; 7 cycles + pop YH + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies + +bitstuffN: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] + nop2 ;[7] + nop ;[9] + out USBOUT, x1 ;[10] <-- out + rjmp didStuffN ;[0] + +bitstuff6: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] Carry is zero due to brcc + rol shift ;[7] compensate for ror shift at branch destination + rjmp didStuff6 ;[8] + +bitstuff7: + ldi x2, 0 ;[2] Carry is zero due to brcc + rjmp didStuff7 ;[3] + + +sendNakAndReti: + ldi x3, USBPID_NAK ;[-18] + rjmp sendX3AndReti ;[-17] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov x3, cnt ;[-16] +sendX3AndReti: + ldi YL, 20 ;[-15] x3==r20 address is 20 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +;We don't match the transfer rate exactly (don't insert leap cycles every third +;byte) because the spec demands only 1.5% precision anyway. +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-6] exor mask + ldi shift, 0x80 ;[-5] sync byte is first byte sent +txByteLoop: + ldi bitcnt, 0x35 ;[-4] [6] binary 0011 0101 +txBitLoop: + sbrs shift, 0 ;[-3] [7] + eor x1, x4 ;[-2] [8] + out USBOUT, x1 ;[-1] [9] <-- out N + ror shift ;[0] [10] + ror x2 ;[1] +didStuffN: + cpi x2, 0xfc ;[2] + brcc bitstuffN ;[3] + lsr bitcnt ;[4] + brcc txBitLoop ;[5] + brne txBitLoop ;[6] + + sbrs shift, 0 ;[7] + eor x1, x4 ;[8] +didStuff6: + out USBOUT, x1 ;[-1] [9] <-- out 6 + ror shift ;[0] [10] + ror x2 ;[1] + cpi x2, 0xfc ;[2] + brcc bitstuff6 ;[3] + ror shift ;[4] +didStuff7: + ror x2 ;[5] + sbrs x2, 7 ;[6] + eor x1, x4 ;[7] + nop ;[8] + cpi x2, 0xfc ;[9] + out USBOUT, x1 ;[-1][10] <-- out 7 + brcc bitstuff7 ;[0] [11] + ld shift, y+ ;[1] + dec cnt ;[3] + brne txByteLoop ;[4] +;make SE0: + cbr x1, USBMASK ;[5] prepare SE0 [spec says EOP may be 21 to 25 cycles] + lds x2, usbNewDeviceAddr;[6] + lsl x2 ;[8] we compare with left shifted address + subi YL, 20 + 2 ;[9] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[10] + out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[0] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< max 52 cycles interrupt disable +;max stack usage: [ret(2), r0, SREG, YL, YH, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 16.5 MHz -> 11 cycles per bit +; 16.3125 MHz < F_CPU < 16.6875 MHz (+/- 1.1%) +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], r0, YH, shift, x1, x2, x3, x4, cnt + push YL ;[-23] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-21] + push YL ;[-20] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-15] + rjmp foundK ;[-14] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-12] +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push r0 ;[-12] +; [---] ;[-11] + push YH ;[-10] +; [---] ;[-9] + lds YL, usbInputBufOffset;[-8] +; [---] ;[-7] + clr YH ;[-6] + subi YL, lo8(-(usbRxBuf));[-5] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-4] [rx loop init] + mov r0, x2 ;[-3] [rx loop init] + sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) + rjmp haveTwoBitsK ;[-1] + pop YH ;[0] undo the pushes from before + pop r0 ;[2] + rjmp waitForK ;[4] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 22 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: ;[1] + push shift ;[1] + push x1 ;[3] + push x2 ;[5] + push x3 ;[7] + ldi shift, 0xff ;[9] [rx loop init] + ori x3, 0xff ;[10] [rx loop init] == ser x3, clear zero flag + + in x1, USBIN ;[11] <-- sample bit 0 + bst x1, USBMINUS ;[12] + bld shift, 0 ;[13] + push x4 ;[14] == phase +; [---] ;[15] + push cnt ;[16] +; [---] ;[17] + ldi phase, 0 ;[18] [rx loop init] + ldi cnt, USB_BUFSIZE;[19] [rx loop init] + rjmp rxbit1 ;[20] +; [---] ;[21] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +/* +byte oriented operations done during loop: +bit 0: store data +bit 1: SE0 check +bit 2: overflow check +bit 3: catch up +bit 4: rjmp to achieve conditional jump range +bit 5: PLL +bit 6: catch up +bit 7: jump, fixup bitstuff +; 87 [+ 2] cycles +------------------------------------------------------------------ +*/ +continueWithBit5: + in x2, USBIN ;[055] <-- bit 5 + eor r0, x2 ;[056] + or phase, r0 ;[057] + sbrc phase, USBMINUS ;[058] + lpm ;[059] optional nop3; modifies r0 + in phase, USBIN ;[060] <-- phase + eor x1, x2 ;[061] + bst x1, USBMINUS ;[062] + bld shift, 5 ;[063] + andi shift, 0x3f ;[064] + in x1, USBIN ;[065] <-- bit 6 + breq unstuff5 ;[066] *** unstuff escape + eor phase, x1 ;[067] + eor x2, x1 ;[068] + bst x2, USBMINUS ;[069] + bld shift, 6 ;[070] +didUnstuff6: ;[ ] + in r0, USBIN ;[071] <-- phase + cpi shift, 0x02 ;[072] + brlo unstuff6 ;[073] *** unstuff escape +didUnstuff5: ;[ ] + nop2 ;[074] +; [---] ;[075] + in x2, USBIN ;[076] <-- bit 7 + eor x1, x2 ;[077] + bst x1, USBMINUS ;[078] + bld shift, 7 ;[079] +didUnstuff7: ;[ ] + eor r0, x2 ;[080] + or phase, r0 ;[081] + in r0, USBIN ;[082] <-- phase + cpi shift, 0x04 ;[083] + brsh rxLoop ;[084] +; [---] ;[085] +unstuff7: ;[ ] + andi x3, ~0x80 ;[085] + ori shift, 0x80 ;[086] + in x2, USBIN ;[087] <-- sample stuffed bit 7 + nop ;[088] + rjmp didUnstuff7 ;[089] +; [---] ;[090] + ;[080] + +unstuff5: ;[067] + eor phase, x1 ;[068] + andi x3, ~0x20 ;[069] + ori shift, 0x20 ;[070] + in r0, USBIN ;[071] <-- phase + mov x2, x1 ;[072] + nop ;[073] + nop2 ;[074] +; [---] ;[075] + in x1, USBIN ;[076] <-- bit 6 + eor r0, x1 ;[077] + or phase, r0 ;[078] + eor x2, x1 ;[079] + bst x2, USBMINUS ;[080] + bld shift, 6 ;[081] no need to check bitstuffing, we just had one + in r0, USBIN ;[082] <-- phase + rjmp didUnstuff5 ;[083] +; [---] ;[084] + ;[074] + +unstuff6: ;[074] + andi x3, ~0x40 ;[075] + in x1, USBIN ;[076] <-- bit 6 again + ori shift, 0x40 ;[077] + nop2 ;[078] +; [---] ;[079] + rjmp didUnstuff6 ;[080] +; [---] ;[081] + ;[071] + +unstuff0: ;[013] + eor r0, x2 ;[014] + or phase, r0 ;[015] + andi x2, USBMASK ;[016] check for SE0 + in r0, USBIN ;[017] <-- phase + breq didUnstuff0 ;[018] direct jump to se0 would be too long + andi x3, ~0x01 ;[019] + ori shift, 0x01 ;[020] + mov x1, x2 ;[021] mov existing sample + in x2, USBIN ;[022] <-- bit 1 again + rjmp didUnstuff0 ;[023] +; [---] ;[024] + ;[014] + +unstuff1: ;[024] + eor r0, x1 ;[025] + or phase, r0 ;[026] + andi x3, ~0x02 ;[027] + in r0, USBIN ;[028] <-- phase + ori shift, 0x02 ;[029] + mov x2, x1 ;[030] + rjmp didUnstuff1 ;[031] +; [---] ;[032] + ;[022] + +unstuff2: ;[035] + eor r0, x2 ;[036] + or phase, r0 ;[037] + andi x3, ~0x04 ;[038] + in r0, USBIN ;[039] <-- phase + ori shift, 0x04 ;[040] + mov x1, x2 ;[041] + rjmp didUnstuff2 ;[042] +; [---] ;[043] + ;[033] + +unstuff3: ;[043] + in x2, USBIN ;[044] <-- bit 3 again + eor r0, x2 ;[045] + or phase, r0 ;[046] + andi x3, ~0x08 ;[047] + ori shift, 0x08 ;[048] + nop ;[049] + in r0, USBIN ;[050] <-- phase + rjmp didUnstuff3 ;[051] +; [---] ;[052] + ;[042] + +unstuff4: ;[053] + andi x3, ~0x10 ;[054] + in x1, USBIN ;[055] <-- bit 4 again + ori shift, 0x10 ;[056] + rjmp didUnstuff4 ;[057] +; [---] ;[058] + ;[048] + +rxLoop: ;[085] + eor x3, shift ;[086] reconstruct: x3 is 0 at bit locations we changed, 1 at others + in x1, USBIN ;[000] <-- bit 0 + st y+, x3 ;[001] +; [---] ;[002] + eor r0, x1 ;[003] + or phase, r0 ;[004] + eor x2, x1 ;[005] + in r0, USBIN ;[006] <-- phase + ser x3 ;[007] + bst x2, USBMINUS ;[008] + bld shift, 0 ;[009] + andi shift, 0xf9 ;[010] +rxbit1: ;[ ] + in x2, USBIN ;[011] <-- bit 1 + breq unstuff0 ;[012] *** unstuff escape + andi x2, USBMASK ;[013] SE0 check for bit 1 +didUnstuff0: ;[ ] Z only set if we detected SE0 in bitstuff + breq se0 ;[014] + eor r0, x2 ;[015] + or phase, r0 ;[016] + in r0, USBIN ;[017] <-- phase + eor x1, x2 ;[018] + bst x1, USBMINUS ;[019] + bld shift, 1 ;[020] + andi shift, 0xf3 ;[021] +didUnstuff1: ;[ ] + in x1, USBIN ;[022] <-- bit 2 + breq unstuff1 ;[023] *** unstuff escape + eor r0, x1 ;[024] + or phase, r0 ;[025] + subi cnt, 1 ;[026] overflow check + brcs overflow ;[027] + in r0, USBIN ;[028] <-- phase + eor x2, x1 ;[029] + bst x2, USBMINUS ;[030] + bld shift, 2 ;[031] + andi shift, 0xe7 ;[032] +didUnstuff2: ;[ ] + in x2, USBIN ;[033] <-- bit 3 + breq unstuff2 ;[034] *** unstuff escape + eor r0, x2 ;[035] + or phase, r0 ;[036] + eor x1, x2 ;[037] + bst x1, USBMINUS ;[038] + in r0, USBIN ;[039] <-- phase + bld shift, 3 ;[040] + andi shift, 0xcf ;[041] +didUnstuff3: ;[ ] + breq unstuff3 ;[042] *** unstuff escape + nop ;[043] + in x1, USBIN ;[044] <-- bit 4 + eor x2, x1 ;[045] + bst x2, USBMINUS ;[046] + bld shift, 4 ;[047] +didUnstuff4: ;[ ] + eor r0, x1 ;[048] + or phase, r0 ;[049] + in r0, USBIN ;[050] <-- phase + andi shift, 0x9f ;[051] + breq unstuff4 ;[052] *** unstuff escape + rjmp continueWithBit5;[053] +; [---] ;[054] + +macro POP_STANDARD ; 16 cycles + pop cnt + pop x4 + pop x3 + pop x2 + pop x1 + pop shift + pop YH + pop r0 + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies + +bitstuff7: + eor x1, x4 ;[4] + ldi x2, 0 ;[5] + nop2 ;[6] C is zero (brcc) + rjmp didStuff7 ;[8] + +bitstuffN: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] + lpm ;[7] 3 cycle NOP, modifies r0 + out USBOUT, x1 ;[10] <-- out + rjmp didStuffN ;[0] + +#define bitStatus x3 + +sendNakAndReti: + ldi cnt, USBPID_NAK ;[-19] + rjmp sendCntAndReti ;[-18] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov r0, cnt ;[-16] + ldi YL, 0 ;[-15] R0 address is 0 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-6] exor mask + ldi shift, 0x80 ;[-5] sync byte is first byte sent + ldi bitStatus, 0xff ;[-4] init bit loop counter, works for up to 12 bytes +byteloop: +bitloop: + sbrs shift, 0 ;[8] [-3] + eor x1, x4 ;[9] [-2] + out USBOUT, x1 ;[10] [-1] <-- out + ror shift ;[0] + ror x2 ;[1] +didStuffN: + cpi x2, 0xfc ;[2] + brcc bitstuffN ;[3] + nop ;[4] + subi bitStatus, 37 ;[5] 256 / 7 ~=~ 37 + brcc bitloop ;[6] when we leave the loop, bitStatus has almost the initial value + sbrs shift, 0 ;[7] + eor x1, x4 ;[8] + ror shift ;[9] +didStuff7: + out USBOUT, x1 ;[10] <-- out + ror x2 ;[0] + cpi x2, 0xfc ;[1] + brcc bitstuff7 ;[2] + ld shift, y+ ;[3] + dec cnt ;[5] + brne byteloop ;[6] +;make SE0: + cbr x1, USBMASK ;[7] prepare SE0 [spec says EOP may be 21 to 25 cycles] + lds x2, usbNewDeviceAddr;[8] + lsl x2 ;[10] we compare with left shifted address + out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + subi YL, 2 ;[0] Only assign address on data packets, not ACK/NAK in r0 + sbci YH, 0 ;[1] + breq skipAddrAssign ;[2] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 12 cycles per bit +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts +;register use in receive loop to receive the data bytes: +; shift assembles the byte currently being received +; x1 holds the D+ and D- line state +; x2 holds the previous line state +; cnt holds the number of bytes left in the receive buffer +; x3 holds the higher crc byte (see algorithm below) +; x4 is used as temporary register for the crc algorithm +; x5 is used for unstuffing: when unstuffing the last received bit is inverted in shift (to prevent further +; unstuffing calls. In the same time the corresponding bit in x5 is cleared to mark the bit as beening iverted +; zl lower crc value and crc table index +; zh used for crc table accesses + +;-------------------------------------------------------------------------------------------------------------- +; CRC mods: +; table driven crc checker, Z points to table in prog space +; ZL is the lower crc byte, x3 is the higher crc byte +; x4 is used as temp register to store different results +; the initialization of the crc register is not 0xFFFF but 0xFE54. This is because during the receipt of the +; first data byte an virtual zero data byte is added to the crc register, this results in the correct initial +; value of 0xFFFF at beginning of the second data byte before the first data byte is added to the crc. +; The magic number 0xFE54 results form the crc table: At tabH[0x54] = 0xFF = crcH (required) and +; tabL[0x54] = 0x01 -> crcL = 0x01 xor 0xFE = 0xFF +; bitcnt is renamed to x5 and is used for unstuffing purposes, the unstuffing works like in the 12MHz version +;-------------------------------------------------------------------------------------------------------------- +; CRC algorithm: +; The crc register is formed by x3 (higher byte) and ZL (lower byte). The algorithm uses a 'reversed' form +; i.e. that it takes the least significant bit first and shifts to the right. So in fact the highest order +; bit seen from the polynomial devision point of view is the lsb of ZL. (If this sounds strange to you i +; propose a research on CRC :-) ) +; Each data byte received is xored to ZL, the lower crc byte. This byte now builds the crc +; table index. Next the new high byte is loaded from the table and stored in x4 until we have space in x3 +; (its destination). +; Afterwards the lower table is loaded from the table and stored in ZL (the old index is overwritten as +; we don't need it anymore. In fact this is a right shift by 8 bits.) Now the old crc high value is xored +; to ZL, this is the second shift of the old crc value. Now x4 (the temp reg) is moved to x3 and the crc +; calculation is done. +; Prior to the first byte the two CRC register have to be initialized to 0xFFFF (as defined in usb spec) +; however the crc engine also runs during the receipt of the first byte, therefore x3 and zl are initialized +; to a magic number which results in a crc value of 0xFFFF after the first complete byte. +; +; This algorithm is split into the extra cycles of the different bits: +; bit7: XOR the received byte to ZL +; bit5: load the new high byte to x4 +; bit6: load the lower xor byte from the table, xor zl and x3, store result in zl (=the new crc low value) +; move x4 (the new high byte) to x3, the crc value is ready +; + + +macro POP_STANDARD ; 18 cycles + pop ZH + pop ZL + pop cnt + pop x5 + pop x3 + pop x2 + pop x1 + pop shift + pop x4 + endm +macro POP_RETI ; 7 cycles + pop YH + pop YL + out SREG, YL + pop YL + endm + +macro CRC_CLEANUP_AND_CHECK + ; the last byte has already been xored with the lower crc byte, we have to do the table lookup and xor + ; x3 is the higher crc byte, zl the lower one + ldi ZH, hi8(usbCrcTableHigh);[+1] get the new high byte from the table + lpm x2, Z ;[+2][+3][+4] + ldi ZH, hi8(usbCrcTableLow);[+5] get the new low xor byte from the table + lpm ZL, Z ;[+6][+7][+8] + eor ZL, x3 ;[+7] xor the old high byte with the value from the table, x2:ZL now holds the crc value + cpi ZL, 0x01 ;[+8] if the crc is ok we have a fixed remainder value of 0xb001 in x2:ZL (see usb spec) + brne ignorePacket ;[+9] detected a crc fault -> paket is ignored and retransmitted by the host + cpi x2, 0xb0 ;[+10] + brne ignorePacket ;[+11] detected a crc fault -> paket is ignored and retransmitted by the host + endm + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG, YH, [sofError], x4, shift, x1, x2, x3, x5, cnt, ZL, ZH + push YL ;[-28] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-26] + push YL ;[-25] + push YH ;[-23] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-17] + rjmp foundK ;[-16] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-15] +;{3, 5} after falling D- edge, average delay: 4 cycles +;bit0 should be at 30 (2.5 bits) for center sampling. Currently at 4 so 26 cylces till bit 0 sample +;use 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push x4 ;[-14] +; [---] ;[-13] + lds YL, usbInputBufOffset;[-12] used to toggle the two usb receive buffers +; [---] ;[-11] + clr YH ;[-10] + subi YL, lo8(-(usbRxBuf));[-9] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-8] [rx loop init] + push shift ;[-7] +; [---] ;[-6] + ldi shift, 0x80 ;[-5] the last bit is the end of byte marker for the pid receiver loop + clc ;[-4] the carry has to be clear for receipt of pid bit 0 + sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) + rjmp haveTwoBitsK ;[-2] + pop shift ;[-1] undo the push from before + pop x4 ;[1] + rjmp waitForK ;[3] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 24 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[0] + push x2 ;[2] + push x3 ;[4] crc high byte + ldi x2, 1< jump back and store the byte + ori shift, 0x01 ;[11] invert the last received bit to prevent furhter unstuffing + in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + andi x5, 0xFE ;[1] mark this bit as inverted (will be corrected before storing shift) + eor x1, x2 ;[2] x1 and x2 have to be different because the stuff bit is always a zero + andi x1, USBMASK ;[3] mask the interesting bits + breq stuffErr ;[4] if the stuff bit is a 1-bit something went wrong + mov x1, x2 ;[5] the next bit expects the last state to be in x1 + rjmp didunstuff0 ;[6] + ;[7] jump delay of rjmp didunstuffX + +unstuff1: ;[11] this is the jump delay of breq unstuffX + in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + ori shift, 0x02 ;[1] invert the last received bit to prevent furhter unstuffing + andi x5, 0xFD ;[2] mark this bit as inverted (will be corrected before storing shift) + eor x2, x1 ;[3] x1 and x2 have to be different because the stuff bit is always a zero + andi x2, USBMASK ;[4] mask the interesting bits + breq stuffErr ;[5] if the stuff bit is a 1-bit something went wrong + mov x2, x1 ;[6] the next bit expects the last state to be in x2 + nop2 ;[7] + ;[8] + rjmp didunstuff1 ;[9] + ;[10] jump delay of rjmp didunstuffX + +unstuff2: ;[9] this is the jump delay of breq unstuffX + ori shift, 0x04 ;[10] invert the last received bit to prevent furhter unstuffing + andi x5, 0xFB ;[11] mark this bit as inverted (will be corrected before storing shift) + in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero + andi x1, USBMASK ;[2] mask the interesting bits + breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong + mov x1, x2 ;[4] the next bit expects the last state to be in x1 + nop2 ;[5] + ;[6] + rjmp didunstuff2 ;[7] + ;[8] jump delay of rjmp didunstuffX + +unstuff3: ;[9] this is the jump delay of breq unstuffX + ori shift, 0x08 ;[10] invert the last received bit to prevent furhter unstuffing + andi x5, 0xF7 ;[11] mark this bit as inverted (will be corrected before storing shift) + in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero + andi x2, USBMASK ;[2] mask the interesting bits + breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong + mov x2, x1 ;[4] the next bit expects the last state to be in x2 + nop2 ;[5] + ;[6] + rjmp didunstuff3 ;[7] + ;[8] jump delay of rjmp didunstuffX + + + +; the include has to be here due to branch distance restirctions +#define __USE_CRC__ +#include "asmcommon.inc" + + + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies +; 7.5 bit times is 90 cycles. ...there is plenty of time + + +sendNakAndReti: + ldi x3, USBPID_NAK ;[-18] + rjmp sendX3AndReti ;[-17] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov x3, cnt ;[-16] +sendX3AndReti: + ldi YL, 20 ;[-15] x3==r20 address is 20 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent + +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-6] <- acquire bus + ldi x2, 0 ;[-6] init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-5] exor mask + ldi shift, 0x80 ;[-4] sync byte is first byte sent +txByteLoop: + ldi bitcnt, 0x40 ;[-3]=[9] binary 01000000 +txBitLoop: ; the loop sends the first 7 bits of the byte + sbrs shift, 0 ;[-2]=[10] if we have to send a 1 don't change the line state + eor x1, x4 ;[-1]=[11] + out USBOUT, x1 ;[0] + ror shift ;[1] + ror x2 ;[2] transfers the last sent bit to the stuffing history +didStuffN: + nop ;[3] + nop ;[4] + cpi x2, 0xfc ;[5] if we sent six consecutive ones + brcc bitstuffN ;[6] + lsr bitcnt ;[7] + brne txBitLoop ;[8] restart the loop while the 1 is still in the bitcount + +; transmit bit 7 + sbrs shift, 0 ;[9] + eor x1, x4 ;[10] +didStuff7: + ror shift ;[11] + out USBOUT, x1 ;[0] transfer bit 7 to the pins + ror x2 ;[1] move the bit into the stuffing history + cpi x2, 0xfc ;[2] + brcc bitstuff7 ;[3] + ld shift, y+ ;[4] get next byte to transmit + dec cnt ;[5] decrement byte counter + brne txByteLoop ;[7] if we have more bytes start next one + ;[8] branch delay + +;make SE0: + cbr x1, USBMASK ;[8] prepare SE0 [spec says EOP may be 25 to 30 cycles] + lds x2, usbNewDeviceAddr;[9] + lsl x2 ;[11] we compare with left shifted address + out USBOUT, x1 ;[0] <-- out SE0 -- from now 2 bits = 24 cycles until bus idle + subi YL, 20 + 2 ;[1] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[2] +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[3] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< +int main (int argc, char **argv) +{ + int i, j; + for (i=0; i<512; i++){ + unsigned short crc = i & 0xff; + for(j=0; j<8; j++) crc = (crc >> 1) ^ ((crc & 1) ? 0xa001 : 0); + if((i & 7) == 0) printf("\n.byte "); + printf("0x%02x, ", (i > 0xff ? (crc >> 8) : crc) & 0xff); + if(i == 255) printf("\n"); + } + return 0; +} + +// Use the following algorithm to compute CRC values: +ushort computeCrc(uchar *msg, uchar msgLen) +{ + uchar i; + ushort crc = 0xffff; + for(i = 0; i < msgLen; i++) + crc = usbCrcTable16[lo8(crc) ^ msg[i]] ^ hi8(crc); + return crc; +} +*/ + +.balign 256 +usbCrcTableLow: +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 + +; .balign 256 +usbCrcTableHigh: +.byte 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2 +.byte 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04 +.byte 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E +.byte 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8 +.byte 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A +.byte 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC +.byte 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6 +.byte 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10 +.byte 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32 +.byte 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4 +.byte 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE +.byte 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38 +.byte 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA +.byte 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C +.byte 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26 +.byte 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0 +.byte 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62 +.byte 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4 +.byte 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE +.byte 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68 +.byte 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA +.byte 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C +.byte 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76 +.byte 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0 +.byte 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92 +.byte 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54 +.byte 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E +.byte 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98 +.byte 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A +.byte 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C +.byte 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86 +.byte 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 + diff --git a/bootloader/usbdrv/usbdrvasm20.inc b/bootloader/usbdrv/usbdrvasm20.inc new file mode 100644 index 0000000..303abaf --- /dev/null +++ b/bootloader/usbdrv/usbdrvasm20.inc @@ -0,0 +1,360 @@ +/* Name: usbdrvasm20.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Jeroen Benschop + * Based on usbdrvasm16.inc from Christian Starkjohann + * Creation Date: 2008-03-05 + * Tabsize: 4 + * Copyright: (c) 2008 by Jeroen Benschop and OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * Revision: $Id: usbdrvasm20.inc 740 2009-04-13 18:23:31Z cs $ + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 20 MHz version of the asssembler part of the USB driver. It +requires a 20 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! +*/ + +#define leap2 x3 +#ifdef __IAR_SYSTEMS_ASM__ +#define nextInst $+2 +#else +#define nextInst .+0 +#endif + +;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 20 MHz -> 13.333333 cycles per bit, 106.666667 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts +;register use in receive loop: +; shift assembles the byte currently being received +; x1 holds the D+ and D- line state +; x2 holds the previous line state +; x4 (leap) is used to add a leap cycle once every three bytes received +; X3 (leap2) is used to add a leap cycle once every three stuff bits received +; bitcnt is used to determine when a stuff bit is due +; cnt holds the number of bytes left in the receive buffer + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt + push YL ;[-28] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-26] + push YL ;[-25] + push YH ;[-23] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-19] + rjmp foundK ;[-18] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-16] +;{3, 5} after falling D- edge, average delay: 4 cycles +;bit0 should be at 34 for center sampling. Currently at 4 so 30 cylces till bit 0 sample +;use 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push bitcnt ;[-16] +; [---] ;[-15] + lds YL, usbInputBufOffset;[-14] +; [---] ;[-13] + clr YH ;[-12] + subi YL, lo8(-(usbRxBuf));[-11] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init] + push shift ;[-9] +; [---] ;[-8] + ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected + nop2 ;[-6] +; [---] ;[-5] + ldi bitcnt, 5 ;[-4] [rx loop init] + sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) + rjmp haveTwoBitsK ;[-2] + pop shift ;[-1] undo the push from before + pop bitcnt ;[1] + rjmp waitForK ;[3] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 27 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[0] + push x2 ;[2] + push x3 ;[4] (leap2) + ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit + push x4 ;[7] == leap + ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received + push cnt ;[10] + ldi cnt, USB_BUFSIZE ;[12] [rx loop init] + ldi x2, 1< +#ifndef __IAR_SYSTEMS_ASM__ +# include +#endif + +#define __attribute__(arg) /* not supported on IAR */ + +#ifdef __IAR_SYSTEMS_ASM__ +# define __ASSEMBLER__ /* IAR does not define standard macro for asm */ +#endif + +#ifdef __HAS_ELPM__ +# define PROGMEM __farflash +#else +# define PROGMEM __flash +#endif + +#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) + +/* The following definitions are not needed by the driver, but may be of some + * help if you port a gcc based project to IAR. + */ +#define cli() __disable_interrupt() +#define sei() __enable_interrupt() +#define wdt_reset() __watchdog_reset() +#define _BV(x) (1 << (x)) + +/* assembler compatibility macros */ +#define nop2 rjmp $+2 /* jump to next instruction */ +#define XL r26 +#define XH r27 +#define YL r28 +#define YH r29 +#define ZL r30 +#define ZH r31 +#define lo8(x) LOW(x) +#define hi8(x) (((x)>>8) & 0xff) /* not HIGH to allow XLINK to make a proper range check */ + +/* Depending on the device you use, you may get problems with the way usbdrv.h + * handles the differences between devices. Since IAR does not use #defines + * for MCU registers, we can't check for the existence of a particular + * register with an #ifdef. If the autodetection mechanism fails, include + * definitions for the required USB_INTR_* macros in your usbconfig.h. See + * usbconfig-prototype.h and usbdrv.h for details. + */ + +/* ------------------------------------------------------------------------- */ +#elif __CODEVISIONAVR__ /* check for CodeVision AVR */ +/* ------------------------------------------------------------------------- */ +/* This port is not working (yet) */ + +/* #define F_CPU _MCU_CLOCK_FREQUENCY_ seems to be defined automatically */ + +#include +#include + +#define __attribute__(arg) /* not supported on IAR */ + +#define PROGMEM __flash +#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) + +#ifndef __ASSEMBLER__ +static inline void cli(void) +{ + #asm("cli"); +} +static inline void sei(void) +{ + #asm("sei"); +} +#endif +#define _delay_ms(t) delay_ms(t) +#define _BV(x) (1 << (x)) +#define USB_CFG_USE_SWITCH_STATEMENT 1 /* macro for if() cascase fails for unknown reason */ + +#define macro .macro +#define endm .endmacro +#define nop2 rjmp .+0 /* jump to next instruction */ + +/* ------------------------------------------------------------------------- */ +#else /* default development environment is avr-gcc/avr-libc */ +/* ------------------------------------------------------------------------- */ + +#include +#ifdef __ASSEMBLER__ +# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ +#else +# include +#endif + +#if USB_CFG_DRIVER_FLASH_PAGE +# define USB_READ_FLASH(addr) pgm_read_byte_far(((long)USB_CFG_DRIVER_FLASH_PAGE << 16) | (long)(addr)) +#else +# define USB_READ_FLASH(addr) pgm_read_byte(addr) +#endif + +#define macro .macro +#define endm .endm +#define nop2 rjmp .+0 /* jump to next instruction */ + +#endif /* development environment */ + +/* for conveniecne, ensure that PRG_RDB exists */ +#ifndef PRG_RDB +# define PRG_RDB(addr) USB_READ_FLASH(addr) +#endif +#endif /* __usbportability_h_INCLUDED__ */ diff --git a/host software/HIDSerialMonitor.pde b/host software/HIDSerialMonitor.pde new file mode 100644 index 0000000..f12006d --- /dev/null +++ b/host software/HIDSerialMonitor.pde @@ -0,0 +1,210 @@ +/***************************************************** + Host software for HID-class USB serial communication. + It allows connecting to a HID serial device (16c0:05df), + sending (up to 32 bytes per packet) and receiving + strings from the device. The code uses a slightly modified + version of the g4p library for user interface, and the + HIDAPI library for handling HID requests. + + The code was initially written by Chaniel Chadowitz + and modified by Ray Wang (Rayshobby LLC). The code is published + under Creative Commons Attribution-ShareAlike 3.0 license. +*************************************************************/ +import g4p_controls.*; +import com.codeminders.hidapi.HIDDeviceInfo; +import com.codeminders.hidapi.HIDManager; +import com.codeminders.hidapi.HIDDevice; +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; +import java.awt.Font; + +GTextArea outputField; +GTextField inputField; +GButton sendButton; +GButton connectButton; +GButton pauseButton; + +boolean paused; +String serialText = new String(); + +int VENDOR_ID = 0x16c0; +int PRODUCT_ID = 0x05df; +HIDDevice device = null; +Boolean device_initialized = false; + +public void setup() { + size(405, 500); + G4P.setGlobalColorScheme(5); + outputField = new GTextArea(this, 0, 100, 405, 400, G4P.SCROLLBARS_VERTICAL_ONLY); + // Set some default text + outputField.setText("Connect to device first.\n"); + outputField.setDefaultText("Connect to device first.\n"); + outputField.setTextEditEnabled(true); + outputField.setFont(new Font("Monospaced", Font.PLAIN, 12)); + inputField = new GTextField(this, 10, 50, 280, 20); + inputField.setText(""); + inputField.setDefaultText(""); + inputField.setFont(new Font("Monospaced", Font.PLAIN, 12)); + + GLabel outputLabel = new GLabel(this, 10, 80, 60, 20, "Output:"); + outputLabel.setTextBold(); + + sendButton = new GButton(this, 300, 50, 100, 20, "Send"); + sendButton.setTextBold(); + sendButton.setEnabled(false); + + GLabel title = new GLabel(this, 100, 10, 200, 30, "HID Serial Monitor"); + title.setTextBold(); + + connectButton = new GButton(this, 10, 15, 100, 20, "Connect"); + connectButton.setTextBold(); + + pauseButton = new GButton(this, 300, 15, 100, 20, "Pause"); + pauseButton.setTextBold(); + pauseButton.setEnabled(false); + + device_initialized = false; +} + +public void draw() { + background(230,230,230); + if( device != null && !paused ) { + String result = deviceRead(); + if( result != null ) { + outputField.appendText(result); + } + } +} + +public void handleTextEvents(GEditableTextControl textarea, GEvent event) { + if( textarea.equals(inputField) && event == GEvent.ENTERED ) { + send(); + } +} + +public void deviceInitialize() { + if (!device_initialized) { + device_initialized = true; + com.codeminders.hidapi.ClassPathLibraryLoader + .loadNativeHIDLibrary(); + } +} + +public void deviceFindFirst() { + deviceInitialize(); + HIDDeviceInfo[] infos = deviceFindAllDescriptors(); + + if (infos.length > 0) { + try { + device = infos[0].open(); + } catch (Exception e) { + device = null; + } + } +} + +public HIDDeviceInfo[] deviceFindAllDescriptors() { + deviceInitialize(); + + List devlist = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + devlist.add(info); + } + } + } catch (Exception e) { + } + + return devlist.toArray(new HIDDeviceInfo[devlist.size()]); +} + +public String deviceRead() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if (read > 0) { + String str = new String(); + for(int i=0;i 32) ? 32 : charArray.length; + byte[] data = new byte[33]; + data[0] = (byte)0; + int i; + for(i=0;i 0) { + try { + device = infos[0].open(); + } catch (Exception e) { + device = null; + } + } +} + +public HIDDeviceInfo[] deviceFindAllDescriptors() { + deviceInitialize(); + + List devlist = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + devlist.add(info); + } + } + } catch (Exception e) { + } + + return devlist.toArray(new HIDDeviceInfo[devlist.size()]); +} + +public String deviceRead() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if (read > 0) { + String str = new String(); + for(int i=0;i 32) ? 32 : charArray.length; + byte[] data = new byte[33]; + data[0] = (byte)0; + int i; + for(i=0;i 0) { + try { + device = infos[0].open(); + } catch (Exception e) { + device = null; + } + } +} + +public HIDDeviceInfo[] deviceFindAllDescriptors() { + deviceInitialize(); + + List devlist = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + devlist.add(info); + } + } + } catch (Exception e) { + } + + return devlist.toArray(new HIDDeviceInfo[devlist.size()]); +} + +public String deviceRead() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if (read > 0) { + String str = new String(); + for(int i=0;i 32) ? 32 : charArray.length; + byte[] data = new byte[33]; + data[0] = (byte)0; + int i; + for(i=0;i 0) { + try { + device = infos[0].open(); + } catch (Exception e) { + device = null; + } + } +} + +public HIDDeviceInfo[] deviceFindAllDescriptors() { + deviceInitialize(); + + List devlist = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + devlist.add(info); + } + } + } catch (Exception e) { + } + + return devlist.toArray(new HIDDeviceInfo[devlist.size()]); +} + +public String deviceRead() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if (read > 0) { + String str = new String(); + for(int i=0;i 32) ? 32 : charArray.length; + byte[] data = new byte[33]; + data[0] = (byte)0; + int i; + for(i=0;i 0) { + try { + device = infos[0].open(); + } catch (Exception e) { + device = null; + } + } +} + +public HIDDeviceInfo[] deviceFindAllDescriptors() { + deviceInitialize(); + + List devlist = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + devlist.add(info); + } + } + } catch (Exception e) { + } + + return devlist.toArray(new HIDDeviceInfo[devlist.size()]); +} + +public String deviceRead() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if (read > 0) { + String str = new String(); + for(int i=0;i 32) ? 32 : charArray.length; + byte[] data = new byte[33]; + data[0] = (byte)0; + int i; + for(i=0;i + + + + CFBundleName + HIDSerialMonitor + CFBundleVersion + 1.0 + CFBundleAllowMixedLocalizations + true + CFBundleExecutable + JavaApplicationStub + CFBundleDevelopmentRegion + English + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleInfoDictionaryVersion + 6.0 + CFBundleIconFile + sketch.icns + CFBundleIdentifier + HIDSerialMonitor + + + LSUIPresentationMode + 0 + + LSArchitecturePriority + + x86_64 + i386 + + + Java + + VMOptions + + + MainClass + HIDSerialMonitor + + + JVMVersion + 1.6* + + ClassPath + $JAVAROOT/HIDSerialMonitor.jar:$JAVAROOT/core.jar:$JAVAROOT/jogl-all.jar:$JAVAROOT/gluegen-rt.jar:$JAVAROOT/jogl-all-natives-macosx-universal.jar:$JAVAROOT/gluegen-rt-natives-macosx-universal.jar:$JAVAROOT/core.jar:$JAVAROOT/G4P.jar:$JAVAROOT/hidapi.jar + + + Properties + + apple.laf.useScreenMenuBar + true + apple.awt.showGrowBox + false + com.apple.smallTabs + true + apple.awt.Antialiasing + false + apple.awt.TextAntialiasing + true + com.apple.hwaccel + true + apple.awt.use-file-dialog-packages + false + + + + diff --git a/host software/application.macosx/HIDSerialMonitor.app/Contents/MacOS/JavaApplicationStub b/host software/application.macosx/HIDSerialMonitor.app/Contents/MacOS/JavaApplicationStub new file mode 100755 index 0000000..56ec300 Binary files /dev/null and b/host software/application.macosx/HIDSerialMonitor.app/Contents/MacOS/JavaApplicationStub differ diff --git a/host software/application.macosx/HIDSerialMonitor.app/Contents/PkgInfo b/host software/application.macosx/HIDSerialMonitor.app/Contents/PkgInfo new file mode 100644 index 0000000..bd04210 --- /dev/null +++ b/host software/application.macosx/HIDSerialMonitor.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/G4P.jar b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/G4P.jar new file mode 100644 index 0000000..8b3bc76 Binary files /dev/null and b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/G4P.jar differ diff --git a/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/HIDSerialMonitor.jar b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/HIDSerialMonitor.jar new file mode 100644 index 0000000..99a104c Binary files /dev/null and b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/HIDSerialMonitor.jar differ diff --git a/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/core.jar b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/core.jar new file mode 100644 index 0000000..4adc0d0 Binary files /dev/null and b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/core.jar differ diff --git a/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/gluegen-rt-natives-macosx-universal.jar b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/gluegen-rt-natives-macosx-universal.jar new file mode 100644 index 0000000..388c697 Binary files /dev/null and b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/gluegen-rt-natives-macosx-universal.jar differ diff --git a/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/gluegen-rt.jar b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/gluegen-rt.jar new file mode 100644 index 0000000..481fb80 Binary files /dev/null and b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/gluegen-rt.jar differ diff --git a/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/hidapi.jar b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/hidapi.jar new file mode 100644 index 0000000..55f332e Binary files /dev/null and b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/hidapi.jar differ diff --git a/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/jogl-all-natives-macosx-universal.jar b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/jogl-all-natives-macosx-universal.jar new file mode 100644 index 0000000..9636d82 Binary files /dev/null and b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/jogl-all-natives-macosx-universal.jar differ diff --git a/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/jogl-all.jar b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/jogl-all.jar new file mode 100644 index 0000000..318ee02 Binary files /dev/null and b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/Java/jogl-all.jar differ diff --git a/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/sketch.icns b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/sketch.icns new file mode 100644 index 0000000..2bdb4df Binary files /dev/null and b/host software/application.macosx/HIDSerialMonitor.app/Contents/Resources/sketch.icns differ diff --git a/host software/application.macosx/source/HIDSerialMonitor.java b/host software/application.macosx/source/HIDSerialMonitor.java new file mode 100644 index 0000000..838ced1 --- /dev/null +++ b/host software/application.macosx/source/HIDSerialMonitor.java @@ -0,0 +1,232 @@ +import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; + +import g4p_controls.*; +import com.codeminders.hidapi.HIDDeviceInfo; +import com.codeminders.hidapi.HIDManager; +import com.codeminders.hidapi.HIDDevice; +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; +import java.awt.Font; + +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +public class HIDSerialMonitor extends PApplet { + + + + + + + + + + +GTextArea outputField; +GTextField inputField; +GButton sendButton; +GButton connectButton; +GButton pauseButton; + +boolean paused; +String serialText = new String(); + +int VENDOR_ID = 0x16c0; +int PRODUCT_ID = 0x05df; +HIDDevice device = null; +Boolean device_initialized = false; + +public void setup() { + size(405, 500); + G4P.setGlobalColorScheme(5); + outputField = new GTextArea(this, 0, 100, 405, 400, G4P.SCROLLBARS_VERTICAL_ONLY); + // Set some default text + outputField.setText("Connect to device first.\n"); + outputField.setDefaultText("Connect to device first.\n"); + outputField.setTextEditEnabled(true); + outputField.setFont(new Font("Monospaced", Font.PLAIN, 12)); + inputField = new GTextField(this, 10, 50, 280, 20); + inputField.setText(""); + inputField.setDefaultText(""); + inputField.setFont(new Font("Monospaced", Font.PLAIN, 12)); + + GLabel outputLabel = new GLabel(this, 10, 80, 60, 20, "Output:"); + outputLabel.setTextBold(); + + sendButton = new GButton(this, 300, 50, 100, 20, "Send"); + sendButton.setTextBold(); + sendButton.setEnabled(false); + + GLabel title = new GLabel(this, 100, 10, 200, 30, "HID Serial Monitor"); + title.setTextBold(); + + connectButton = new GButton(this, 10, 15, 100, 20, "Connect"); + connectButton.setTextBold(); + + pauseButton = new GButton(this, 300, 15, 100, 20, "Pause"); + pauseButton.setTextBold(); + pauseButton.setEnabled(false); + + device_initialized = false; +} + +public void draw() { + background(230,230,230); + if( device != null && !paused ) { + String result = deviceRead(); + if( result != null ) { + outputField.appendText(result); + } + } +} + +public void handleTextEvents(GEditableTextControl textarea, GEvent event) { + if( textarea.equals(inputField) && event == GEvent.ENTERED ) { + send(); + } +} + +public void deviceInitialize() { + if (!device_initialized) { + device_initialized = true; + com.codeminders.hidapi.ClassPathLibraryLoader + .loadNativeHIDLibrary(); + } +} + +public void deviceFindFirst() { + deviceInitialize(); + HIDDeviceInfo[] infos = deviceFindAllDescriptors(); + + if (infos.length > 0) { + try { + device = infos[0].open(); + } catch (Exception e) { + device = null; + } + } +} + +public HIDDeviceInfo[] deviceFindAllDescriptors() { + deviceInitialize(); + + List devlist = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + devlist.add(info); + } + } + } catch (Exception e) { + } + + return devlist.toArray(new HIDDeviceInfo[devlist.size()]); +} + +public String deviceRead() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if (read > 0) { + String str = new String(); + for(int i=0;i 32) ? 32 : charArray.length; + byte[] data = new byte[33]; + data[0] = (byte)0; + int i; + for(i=0;i 0) { + try { + device = infos[0].open(); + } catch (Exception e) { + device = null; + } + } +} + +public HIDDeviceInfo[] deviceFindAllDescriptors() { + deviceInitialize(); + + List devlist = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + devlist.add(info); + } + } + } catch (Exception e) { + } + + return devlist.toArray(new HIDDeviceInfo[devlist.size()]); +} + +public String deviceRead() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if (read > 0) { + String str = new String(); + for(int i=0;i 32) ? 32 : charArray.length; + byte[] data = new byte[33]; + data[0] = (byte)0; + int i; + for(i=0;i 0) { + try { + device = infos[0].open(); + } catch (Exception e) { + device = null; + } + } +} + +public HIDDeviceInfo[] deviceFindAllDescriptors() { + deviceInitialize(); + + List devlist = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + devlist.add(info); + } + } + } catch (Exception e) { + } + + return devlist.toArray(new HIDDeviceInfo[devlist.size()]); +} + +public String deviceRead() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if (read > 0) { + String str = new String(); + for(int i=0;i 32) ? 32 : charArray.length; + byte[] data = new byte[33]; + data[0] = (byte)0; + int i; + for(i=0;i 0) { + try { + device = infos[0].open(); + } catch (Exception e) { + device = null; + } + } +} + +public HIDDeviceInfo[] deviceFindAllDescriptors() { + deviceInitialize(); + + List devlist = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + devlist.add(info); + } + } + } catch (Exception e) { + } + + return devlist.toArray(new HIDDeviceInfo[devlist.size()]); +} + +public String deviceRead() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if (read > 0) { + String str = new String(); + for(int i=0;i 32) ? 32 : charArray.length; + byte[] data = new byte[33]; + data[0] = (byte)0; + int i; + for(i=0;i 0) { + try { + device = infos[0].open(); + } catch (Exception e) { + device = null; + } + } +} + +public HIDDeviceInfo[] deviceFindAllDescriptors() { + deviceInitialize(); + + List devlist = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + devlist.add(info); + } + } + } catch (Exception e) { + } + + return devlist.toArray(new HIDDeviceInfo[devlist.size()]); +} + +public String deviceRead() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if (read > 0) { + String str = new String(); + for(int i=0;i 32) ? 32 : charArray.length; + byte[] data = new byte[33]; + data[0] = (byte)0; + int i; + for(i=0;i 0) { + try { + device = infos[0].open(); + } catch (Exception e) { + device = null; + } + } +} + +public HIDDeviceInfo[] deviceFindAllDescriptors() { + deviceInitialize(); + + List devlist = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + devlist.add(info); + } + } + } catch (Exception e) { + } + + return devlist.toArray(new HIDDeviceInfo[devlist.size()]); +} + +public String deviceRead() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if (read > 0) { + String str = new String(); + for(int i=0;i 32) ? 32 : charArray.length; + byte[] data = new byte[33]; + data[0] = (byte)0; + int i; + for(i=0;i= 1000) + sdr.setLocalColorScheme(button.tagNo - 1000); + else if (btnMakeCode == button) + placeCodeOnClipboard(); +} + +public void handleToggleControlEvents(GToggleControl option, GEvent event) { + if (option == optLeft) + sdr.setTextOrientation(G4P.ORIENT_LEFT); + else if (option == optRight) + sdr.setTextOrientation(G4P.ORIENT_RIGHT); + else if (option == optTrack) + sdr.setTextOrientation(G4P.ORIENT_TRACK); + else if (option == cbxOpaque) + sdr.setOpaque(option.isSelected()); + else if (option == cbxValue) + sdr.setShowValue(option.isSelected()); + else if (option == cbxShowTicks) + sdr.setShowTicks(option.isSelected()); + else if (option == cbxLimits) + sdr.setShowLimits(option.isSelected()); + else if (option == cbxSticky) + sdr.setStickToTicks(option.isSelected()); +} + +public void handleDropListEvents(GDropList list, GEvent event) { + if (list == dplStyle) + sdr.setStyle(dplStyle.getSelectedText()); +} + + +private void placeCodeOnClipboard() { + StringBuilder s = new StringBuilder(); + s.append("// Generated by the GKnob example program\n\n"); + s.append("import g4p_controls.*;\n\n"); + s.append("GCustomSlider sdr; \n\n"); + s.append("void setup() { \n"); + s.append(" size(300, 300); \n"); + s.append(" sdr = new GCustomSlider(this, 55, 70, 200, 120, \"" + dplStyle.getSelectedText() + "\"); \n"); + s.append(" // Some of the following statements are not actually\n"); + s.append(" // required because they are setting the default value. \n"); + s.append(" sdr.setLocalColorScheme(" + sdr.getLocalColorScheme() + "); \n"); + s.append(" sdr.setOpaque(" + sdr.isOpaque() + "); \n"); + s.append(" sdr.setValue(" + sdr.getValueF() + "); \n"); + s.append(" sdr.setNbrTicks(" + sdr.getNbrTicks() + "); \n"); + s.append(" sdr.setShowLimits(" + sdr.isShowLimits() + "); \n"); + s.append(" sdr.setShowValue(" + sdr.isShowValue() + "); \n"); + s.append(" sdr.setShowTicks(" + sdr.isShowTicks() + "); \n"); + s.append(" sdr.setStickToTicks(" + sdr.isStickToTicks() + "); \n"); + s.append(" sdr.setEasing(" + sdr.getEasing() + "); \n"); + s.append(" sdr.setRotation(" + knbAngle.getValueF() + ", PApplet.CENTER); \n"); + s.append("} \n\n"); + s.append("void draw(){ \n"); + s.append(" background(" + bgcol + "); \n"); + s.append("} \n"); + if (GClip.copy(s.toString())) + println("Paste code into empty Processing sketch"); + else + System.err.println("UNABLE TO ACCESS CLIPBOARD"); +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_CustomSlider_Config/configcontrols.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_CustomSlider_Config/configcontrols.pde new file mode 100644 index 0000000..3c10abe --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_CustomSlider_Config/configcontrols.pde @@ -0,0 +1,96 @@ +// The code in this tab is used to create all the other +// controls needed to configure the slider control. + +GToggleGroup tg = new GToggleGroup(); +GOption optLeft, optRight, optTrack; +GCheckbox cbxOpaque, cbxShowTicks, cbxValue, cbxLimits, cbxSticky; +GSlider sdrBack, sdrNbrTicks, sdrEasing; +GLabel lblOrient, lblVisible, lblNbrTicks, lblEasing, lblSkin; +GKnob knbAngle; +GButton btnMakeCode; +GButton[] btnColours = new GButton[8]; +GDropList dplStyle; + +public void makeSliderConfigControls() { + // Create colour scheme selectors + int x = width - 42, y = 2; + for (int i = 0; i < btnColours.length; i++) { + btnColours[i] = new GButton(this, x, y + i * 20, 40, 18, "" + (i+1)); + btnColours[i].tag = "Button: " + (i+1); + btnColours[i].setLocalColorScheme(i); + btnColours[i].tagNo = 1000+i; + } + // Create sliders + x = width-100; + y = 162; + bgcol = 200; + sdrBack = new GSlider(this, x, y, 162, 80, 12); + sdrBack.setLimits(bgcol, 0, 255); + sdrBack.setRotation(-PI/2); + sdrBack.setTextOrientation(G4P.ORIENT_RIGHT); + sdrBack.setEasing(20); + sdrBack.setShowValue(true); + sdrBack.setShowTicks(true); + + x = width - 180; + y = 250; + + sdrEasing = new GSlider(this, x, y + 34, 80, 40, 12); + sdrEasing.setLimits(1.0f, 1.0f, 30.0f); + sdrEasing.setShowValue(true); + lblEasing = new GLabel(this, x + 82, y + 34, 80, 40, "Easing"); + lblEasing.setTextAlign(GAlign.LEFT, null); + lblEasing.setTextItalic(); + + sdrNbrTicks = new GSlider(this, x, y + 68, 80, 40, 12); + sdrNbrTicks.setLimits(2, 2, 15); + sdrNbrTicks.setShowValue(true); + lblNbrTicks = new GLabel(this, x + 82, y + 68, 80, 40, "No. of ticks"); + lblNbrTicks.setTextAlign(GAlign.LEFT, null); + lblNbrTicks.setTextItalic(); + + x = width - 180; + y = 164; + lblOrient = new GLabel(this, x, y, 80, 18, "Orient Text"); + lblOrient.setTextAlign(GAlign.LEFT, null); + lblOrient.setTextBold(); + optLeft = new GOption(this, x, y + 20, 80, 18, "Left"); + optRight = new GOption(this, x, y + 40, 80, 18, "Right"); + optTrack = new GOption(this, x, y + 60, 80, 18, "Track"); + tg.addControls(optLeft, optRight, optTrack); + optTrack.setSelected(true); + + x = width - 94; + y = 164; + lblSkin = new GLabel(this, x, y, 80, 18, "Skin"); + lblSkin.setTextBold(); + String[] items = new String[] { + "blue18px", "green_red20px", "grey_blue", "purple18px", "red_yellow18px" + }; + dplStyle = new GDropList(this, x, y + 20, 90, 96, 5); + dplStyle.setItems(items, 0); + + + x = width - 180; + y = 2; + lblVisible = new GLabel(this, x, y, 70, 18, "Visible"); + lblVisible.setTextBold(); + cbxShowTicks = new GCheckbox(this, x, y + 20, 70, 18, "Ticks"); + cbxLimits = new GCheckbox(this, x, y + 40, 70, 18, "Limits"); + cbxValue = new GCheckbox(this, x, y + 60, 70, 18, "Value"); + cbxOpaque = new GCheckbox(this, x, y + 80, 70, 18, "Opaque"); + cbxSticky = new GCheckbox(this, x, y + 110, 70, 40, "Stick to ticks"); + + x = 10; + y = height - 80; + knbAngle = new GKnob(this, x, y, 70, 70, 0.6f); + knbAngle.setTurnRange(0, 360); + knbAngle.setLimits(0.0f, 0.0f, TWO_PI); + knbAngle.setTurnMode(G4P.CTRL_ANGULAR); + knbAngle.setIncludeOverBezel(true); + knbAngle.setNbrTicks(13); + knbAngle.setStickToTicks(true); + + btnMakeCode = new GButton(this, x + 100, y + 20, 182, 38, "Place code for existing configuration on clipboard"); +} + diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Dialogs/G4P_Dialogs.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Dialogs/G4P_Dialogs.pde new file mode 100644 index 0000000..5fea308 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Dialogs/G4P_Dialogs.pde @@ -0,0 +1,376 @@ +/** +Simple sketch to experiment with the different dialogs +available to you in the G4P library. + +The file diaologs will not work in an applet hosted on +a website unless you digitally sign the jars. + +The file dialogs match the behaviour of those in Processing +V1.5.1 i.e. immediate mode rather than on a seperate thread +as in 2.0 + +created by Peter Lager + +*/ + +import java.awt.Rectangle; +import java.util.ArrayList; + +import g4p_controls.*; + +public void setup() { + size(660, 420); + rects = new ArrayList (); + createFileSystemGUI(340, 220, 300, 130, 6); + createOptionDialogGUI(20, 20, 440, 184, 6); + createMessageDialogGUI(20, 220, 300, 184, 6); + createColorChooserGUI(480, 20, 160, 60, 6); +} + +public void draw() { + background(200, 200, 255); + for (Rectangle r : rects) + showFrame(r); +} + +public void handleButtonEvents(GButton button, GEvent event) { + // Folder selection + if (button == btnFolder || button == btnInput || button == btnOutput) + handleFileDialog(button); + else if (button == btnMdialog) + handleMessageDialog(); + // Option dialog + else if (button == btnOdialog) + handleOptionDialog(); + // Color chooser + else if (button == btnColor) + handleColorChooser(); +} + +// G4P code for folder and file dialogs +public void handleFileDialog(GButton button) { + String fname; + // Folder selection + if (button == btnFolder) { + fname = G4P.selectFolder("Folder Dialog"); + lblFile.setText(fname); + } + // File input selection + else if (button == btnInput) { + // Use file filter if possible + fname = G4P.selectInput("Input Dialog", "png,gif,jpg,jpeg", "Image files"); + lblFile.setText(fname); + } + // File output selection + else if (button == btnOutput) { + fname = G4P.selectOutput("Output Dialog"); + lblFile.setText(fname); + } +} + +// G4P code for message dialogs +public void handleMessageDialog() { + // Determine message type + int mtype; + switch(md_mtype) { + default: + case 0: + mtype = G4P.PLAIN; + break; + case 1: + mtype = G4P.ERROR; + break; + case 2: + mtype = G4P.INFO; + break; + case 3: + mtype = G4P.WARNING; + break; + case 4: + mtype = G4P.QUERY; + break; + } + String message = txfSMMessage.getText(); + String title = txfMdTitle.getText(); + G4P.showMessage(this, message, title, mtype); +} + +// G4P code for option dialogs +public void handleOptionDialog() { + // Determine message type + int mtype, otype; + switch(od_mtype) { + default: + case 0: + mtype = G4P.PLAIN; + break; + case 1: + mtype = G4P.ERROR; + break; + case 2: + mtype = G4P.INFO; + break; + case 3: + mtype = G4P.WARNING; + break; + case 4: + mtype = G4P.QUERY; + break; + } + // Determine the option type + switch(od_otype) { + default: + case 0: + otype = G4P.YES_NO; + break; + case 1: + otype = G4P.YES_NO_CANCEL; + break; + case 2: + otype = G4P.OK_CANCEL; + break; + } + String message = txfOdMessage.getText(); + String title = txfOdTitle.getText(); + int reply = G4P.selectOption(this, message, title, mtype, otype); + switch(reply) { + case G4P.OK: + lblReply.setText("OK / YES"); + break; + case G4P.NO: + lblReply.setText("NO"); + break; + case G4P.CANCEL: + lblReply.setText("CANCEL"); + break; + case G4P.CLOSED: + lblReply.setText("CLOSED"); + break; + } + /* + * Some comments on the returned value: + * G4P.OK and G4P.YES have the same integer value so can + * be used interchangeably. + * G4P.CLOSED maybe returned if the dialog box is closed + * although on some systems G4P.NO or G4P.CANCEL may be + * returned instead. + * It is safer to tested for a positive response because + * they have the same value. If you must test for a negative + * response use !G4P.OK or !G4P.YES + */ +} + +// G4P code for colour chooser +public void handleColorChooser() { + sel_col = G4P.selectColor(); + pg.beginDraw(); + pg.background(sel_col); + pg.endDraw(); +} + +// Handles events from checkbox and option controls. +public void handleToggleControlEvents(GToggleControl checkbox, GEvent event) { + if (checkbox == cbxUseNative) { + PApplet.useNativeSelect = cbxUseNative.isSelected(); + } + else if (checkbox.tagNo >= 6000 ) { + switch(checkbox.tagNo / 1000) { + case 6: + od_mtype = checkbox.tagNo % 6000; + break; + case 7: + od_otype = checkbox.tagNo % 7000; + break; + case 9: + md_mtype = checkbox.tagNo % 9000; + break; + } + } +} + +// Simple graphical frame to group controls +public void showFrame(Rectangle r) { + noFill(); + strokeWeight(1); + stroke(color(240, 240, 255)); + rect(r.x, r.y, r.width, r.height); + stroke(color(0)); + rect(r.x+1, r.y+1, r.width, r.height); +} + +// The next 4 methods are simply to create the GUI. So there is +// no more code related to the various dialogs. +public void createColorChooserGUI(int x, int y, int w, int h, int border) { + // Store picture frame + rects.add(new Rectangle(x, y, w, h)); + // Set inner frame position + x += border; + y += border; + w -= 2*border; + h -= 2*border; + GLabel title = new GLabel(this, x, y, w, 20); + title.setText("Color picker dialog", GAlign.LEFT, GAlign.MIDDLE); + title.setOpaque(true); + title.setTextBold(); + btnColor = new GButton(this, x, y+26, 80, 20, "Choose"); + sel_col = color(255); + pg = createGraphics(60, 20, JAVA2D); + pg.beginDraw(); + pg.background(sel_col); + pg.endDraw(); + spad = new GSketchPad(this, x+88, y+26, pg.width, pg.height); + spad.setGraphic(pg); +} + +public void createMessageDialogGUI(int x, int y, int w, int h, int border) { + // Store picture frame + rects.add(new Rectangle(x, y, w, h)); + // Set inner frame position + x += border; + y += border; + w -= 2*border; + h -= 2*border; + GLabel title = new GLabel(this, x, y, w, 20); + title.setText("Message dialogs", GAlign.LEFT, GAlign.MIDDLE); + title.setOpaque(true); + title.setTextBold(); + btnMdialog = new GButton(this, x, y+26, 80, 20, "Show"); + String[] t = new String[] { + "Plain", "Error", "Information", "Warning", "Question" + }; + opgMmessType = new GToggleGroup(); + optMmessType = new GOption[t.length]; + for (int i = 0; i < optMmessType.length; i++) { + optMmessType[i] = new GOption(this, x, y+60+i*18, 94, 18); + optMmessType[i].setText(t[i]); + optMmessType[i].tagNo = 9000 + i; + opgMmessType.addControl(optMmessType[i]); + } + md_mtype = 0; + optMmessType[md_mtype].setSelected(true); + GLabel dtitle = new GLabel(this, x+w-190, y+20, 190, 20); + dtitle.setText("Dialog title", GAlign.LEFT, GAlign.MIDDLE); + txfMdTitle = new GTextField(this, x+w-190, y+40, 190, 20); + txfMdTitle.setDefaultText("Enter dialog title"); + GLabel dmess = new GLabel(this, x+w-190, y+60, 190, 20); + dmess.setText("Dialog message", GAlign.LEFT, GAlign.MIDDLE); + txfSMMessage = new GTextArea(this, x+w-193, y+75, 196, 100); + txfSMMessage.setOpaque(false); + txfSMMessage.setDefaultText("Enter dialog message"); +} + +public void createOptionDialogGUI(int x, int y, int w, int h, int border) { + // Store picture frame + rects.add(new Rectangle(x, y, w, h)); + // Set inner frame position + x += border; + y += border; + w -= 2*border; + h -= 2*border; + GLabel title = new GLabel(this, x, y, w, 20); + title.setText("Option dialogs", GAlign.LEFT, GAlign.MIDDLE); + title.setOpaque(true); + title.setTextBold(); + btnOdialog = new GButton(this, x, y+26, 80, 20, "Show"); + String[] t = new String[] { + "Plain", "Error", "Information", "Warning", "Question" + }; + opgOmessType = new GToggleGroup(); + optOmessType = new GOption[t.length]; + for (int i = 0; i < optOmessType.length; i++) { + optOmessType[i] = new GOption(this, x, y+60+i*18, 94, 18); + optOmessType[i].setText(t[i]); + optOmessType[i].tagNo = 6000 + i; + opgOmessType.addControl(optOmessType[i]); + } + od_mtype = 0; + optOmessType[md_mtype].setSelected(true); + t = new String[] { + "Yes / No", "Yes / No / Cancel", "Ok / Cancel" + }; + opgOoptType = new GToggleGroup(); + optOoptType = new GOption[t.length]; + for (int i = 0; i < optOoptType.length; i++) { + optOoptType[i] = new GOption(this, x+104, y+60+i*18, 140, 18); + optOoptType[i].setText(t[i]); + optOoptType[i].tagNo = 7000 + i; + opgOoptType.addControl(optOoptType[i]); + } + od_otype = 0; + optOoptType[md_mtype].setSelected(true); + lblReply = new GLabel(this, x+104, y+26, 120, 20); + lblReply.setLocalColorScheme(G4P.GREEN_SCHEME); + lblReply.setOpaque(true); + lblReply.setTextAlign(GAlign.CENTER, GAlign.MIDDLE); + GLabel dtitle = new GLabel(this, x+w-190, y+20, 190, 20); + dtitle.setText("Dialog title", GAlign.LEFT, GAlign.MIDDLE); + txfOdTitle = new GTextField(this, x+w-190, y+40, 190, 20); + txfOdTitle.setDefaultText("Enter dialog title"); + GLabel dmess = new GLabel(this, x+w-190, y+60, 190, 20); + dmess.setText("Dialog message", GAlign.LEFT, GAlign.MIDDLE); + txfOdMessage = new GTextArea(this, x+w-193, y+75, 196, 100); + txfOdMessage.setOpaque(false); + txfOdMessage.setDefaultText("Enter dialog message"); +} + +public void createFileSystemGUI(int x, int y, int w, int h, int border) { + // Store picture frame + rects.add(new Rectangle(x, y, w, h)); + // Set inner frame position + x += border; + y += border; + w -= 2*border; + h -= 2*border; + GLabel title = new GLabel(this, x, y, w, 20); + title.setText("File system dialogs", GAlign.LEFT, GAlign.MIDDLE); + title.setOpaque(true); + title.setTextBold(); + // Create buttons + int bgap = 8; + int bw = round((w - 2 * bgap) / 3.0f); + int bs = bgap + bw; + btnFolder = new GButton(this, x, y+30, bw, 20, "Folder"); + btnInput = new GButton(this, x+bs, y+30, bw, 20, "Input"); + btnOutput = new GButton(this, x+2*bs, y+30, bw, 20, "Output"); + lblFile = new GLabel(this, x, y+60, w, 44); + lblFile.setTextAlign(GAlign.LEFT, GAlign.MIDDLE); + lblFile.setOpaque(true); + lblFile.setLocalColorScheme(G4P.GREEN_SCHEME); + // Use native or Java Swing dialogs + cbxUseNative = new GCheckbox(this, x, y + h - 14, w, 20, "Use native controls"); + cbxUseNative.setSelected(true); + cbxUseNative.setTextItalic(); +} + +// Controls used for file dialog GUI +GButton btnFolder, btnInput, btnOutput; +GLabel lblFile; +GCheckbox cbxUseNative; + +// Controls used for message dialog GUI +GButton btnMdialog; +GOption[] optMmessType; +GToggleGroup opgMmessType; +GTextField txfMdTitle; +GTextArea txfSMMessage; +int md_mtype; + +// Controls used for option dialog GUI +GButton btnOdialog; +GOption[] optOmessType, optOoptType; +GToggleGroup opgOmessType, opgOoptType; +GTextField txfOdTitle; +GTextArea txfOdMessage; +GLabel lblReply; +int od_mtype, od_otype; + +// Controls used for colour chooser dialog GUI +GButton btnColor; +GSketchPad spad; +PGraphics pg; +int sel_col = -1; + +// Graphic frames used to group controls +ArrayList rects ; + + diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_EditTextControls/G4P_EditTextControls.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_EditTextControls/G4P_EditTextControls.pde new file mode 100644 index 0000000..41f50e8 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_EditTextControls/G4P_EditTextControls.pde @@ -0,0 +1,76 @@ +/** + * This sketch is to demonstrate some features of the GTextField + * and GTextArea controls. + * These features include + * - Tabbing between controls + * - Default text + * - Copy and paste text + * + * @author Peter Lager + */ + +import g4p_controls.*; + +GTextField txf1, txf2; +GTextArea txa1, txa2; +GTabManager tt; + +public void setup() { + size(500, 260); + G4P.setGlobalColorScheme(GCScheme.RED_SCHEME); + // Some start text + String[] paragraphs = loadStrings("book3.txt"); + String startTextA = PApplet.join(paragraphs, '\n'); + String startTextF = "G4P is a GUI control library created by Peter Lager"; + + txf1 = new GTextField(this, 10, 10, 200, 20); + txf1.tag = "txf1"; + txf1.setDefaultText("Text field 1"); + + txf2 = new GTextField(this, 290, 10, 200, 30, G4P.SCROLLBAR_HORIZONTAL); + txf2.tag = "txf2"; + txf2.setDefaultText("Text field 2"); + txf2.setText(startTextF); + + txa1 = new GTextArea(this, 10, 80, 200, 160); + txa1.tag = "txa1"; + txa1.setDefaultText("Text area 1"); + + txa2 = new GTextArea(this, 290, 80, 200, 160, G4P.SCROLLBARS_BOTH); + txa2.tag = "txa2"; + txa2.setDefaultText("Text area 2"); + txa2.setText(startTextA, 300); + + // Create the tab manager and add these controls to it + tt = new GTabManager(); + tt.addControls(txf1, txa1, txf2, txa2); +} + +public void draw() { + background(240, 240, 200); + // Draw tab order + stroke(0); + strokeWeight(2); + line(txf1.getCX(), txf1.getCY(), txa1.getCX(), txa1.getCY()); + line(txa1.getCX(), txa1.getCY(), txf2.getCX(), txf2.getCY()); + line(txf2.getCX(), txf2.getCY(), txa2.getCX(), txa2.getCY()); +} + +public void handleTextEvents(GEditableTextControl tc, GEvent event) { + System.out.print("\n" + tc.tag + " Event type: "); + switch(event) { + case CHANGED: + System.out.println("CHANGED"); + break; + case SELECTION_CHANGED: + System.out.println("SELECTION_CHANGED"); + System.out.println(tc.getSelectedText() + "\n"); + break; + case ENTERED: + System.out.println("ENTER KEY TYPED"); + System.out.println(tc.getSelectedText() + "\n"); + break; + default: + System.out.println("UNKNOWN"); + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_EditTextControls/data/book3.txt b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_EditTextControls/data/book3.txt new file mode 100644 index 0000000..c6a9d4e --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_EditTextControls/data/book3.txt @@ -0,0 +1,3 @@ +Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. +Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. +It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like) \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/G4P_ImageButtons.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/G4P_ImageButtons.pde new file mode 100644 index 0000000..cf55763 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/G4P_ImageButtons.pde @@ -0,0 +1,65 @@ +/** + Demonstration of image buttons available in the G4P (GUI for + Processing) library. The button face comes from user supplied + image files. + + (c) 2012 Peter Lager + */ + +import g4p_controls.*; + +GImageButton btnGhost, btnCoins, btnTJ, btnInfo; +GLabel lblOut; +long timer; + +void setup() { + size(580, 220); + cursor(CROSS); + String[] files; + + files = new String[] { + "infooff.png", "infoover.png", "infodown.png" + }; + btnInfo = new GImageButton(this, 20, 14, files, "infomask.png"); + + files = new String[] { + "ghost0.png", "ghost1.png", "ghost2.png" + }; + btnGhost = new GImageButton(this, 40, 90, files); + + files = new String[] { + "tjoff.jpg", "tjover.jpg", "tjdown.jpg" + }; + btnTJ = new GImageButton(this, 150, 10, files, "tjmask.png"); + + files = new String[] { + "coins0.png", "coins1.png", "coins2.png" + }; + btnCoins = new GImageButton(this, 400, 20, files); + + lblOut = new GLabel(this, 10, 190, 560, 20, ""); + lblOut.setTextAlign(GAlign.CENTER, null); + timer = millis() - 5000; +} + +void draw() { + background(220, 220, 255); + // Only display button info for 4 seconds + if (millis() - timer > 4000) { + lblOut.setText("CLICK ON A BUTTON"); + } +} + +// When a button has been clicked give information aout the +// button +void handleButtonEvents(GImageButton button, GEvent event) { + if (button == btnGhost) + lblOut.setText("Ghosts - png images using transparency"); + else if (button == btnCoins) + lblOut.setText("Coins - png images using transparency"); + else if (button == btnTJ) + lblOut.setText("Tom & Jerry - jpg images using png mask"); + else if (button == btnInfo) + lblOut.setText("Info - png images using png mask"); + timer = millis(); +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/coins0.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/coins0.png new file mode 100644 index 0000000..ed31c5e Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/coins0.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/coins1.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/coins1.png new file mode 100644 index 0000000..4e7b0f9 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/coins1.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/coins2.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/coins2.png new file mode 100644 index 0000000..c72cdd9 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/coins2.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/ghost0.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/ghost0.png new file mode 100644 index 0000000..535f64d Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/ghost0.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/ghost1.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/ghost1.png new file mode 100644 index 0000000..9dffa60 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/ghost1.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/ghost2.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/ghost2.png new file mode 100644 index 0000000..b3efec2 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/ghost2.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infodown.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infodown.png new file mode 100644 index 0000000..d54f417 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infodown.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infomask.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infomask.png new file mode 100644 index 0000000..840f27a Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infomask.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infooff.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infooff.png new file mode 100644 index 0000000..0a21163 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infooff.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infoover.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infoover.png new file mode 100644 index 0000000..f0e1313 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/infoover.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjdown.jpg b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjdown.jpg new file mode 100644 index 0000000..c0240a2 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjdown.jpg differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjmask.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjmask.png new file mode 100644 index 0000000..2b127d8 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjmask.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjoff.jpg b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjoff.jpg new file mode 100644 index 0000000..dca694d Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjoff.jpg differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjover.jpg b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjover.jpg new file mode 100644 index 0000000..39b7493 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImageButtons/data/tjover.jpg differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/G4P_ImgTogButton.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/G4P_ImgTogButton.pde new file mode 100644 index 0000000..9ed2924 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/G4P_ImgTogButton.pde @@ -0,0 +1,68 @@ +/** + * This program demonstrates how to create image toggle buttons. + * + * If G4P is unable to create the button using the information + * i.e. images then it will create the default two state switch + * button. + * + * (c) 2013 Peter Lager + * + */ + +import g4p_controls.*; + +GImageToggleButton btnToggle0, btnToggle1, btnToggle2, btnToggle3; +GLabel label1, label2, label3, label4, label5; + +public void setup() { + size(480, 220, JAVA2D); + G4P.setGlobalColorScheme(GCScheme.ORANGE_SCHEME); + G4P.setCursor(ARROW); + if (frame != null) + frame.setTitle("Sketch Window"); + // Create image toggle buttons + btnToggle0 = new GImageToggleButton(this, 10, 10); + btnToggle0.tag = "Library default button "; + btnToggle1 = new GImageToggleButton(this, 190, 70, "s2.png", 4, 4); + btnToggle1.tag = "User 16 state button "; + btnToggle2 = new GImageToggleButton(this, 340, 70, "s2.png", "s3.png", 4, 4); + btnToggle2.tag = "User 16 state button with over image "; + btnToggle3 = new GImageToggleButton(this, 10, 90, "bulb.png", 2, 1); + btnToggle3.tag = "Light bulb 2 state button "; + // Create description labels + createLabels(); +} + +public void draw() { + background(250, 225, 200); +} + +// Event handler for image toggle buttons +public void handleToggleButtonEvents(GImageToggleButton button, GEvent event) { + println(button + " State: " + button.stateValue()); +} + +// Create the labels syaing what-is-what +public void createLabels() { + label1 = new GLabel(this, 60, 10, 110, 50); + label1.setText("Library default 2 state button"); + label1.setTextBold(); + label1.setOpaque(true); + label2 = new GLabel(this, 190, 10, 270, 20); + label2.setText("Smiley 16 state button"); + label2.setTextBold(); + label2.setOpaque(true); + label3 = new GLabel(this, 190, 30, 120, 40); + label3.setText("Off image only"); + label3.setTextBold(); + label3.setOpaque(true); + label4 = new GLabel(this, 340, 30, 120, 40); + label4.setText("With mouse over image"); + label4.setTextBold(); + label4.setOpaque(true); + label5 = new GLabel(this, 60, 170, 100, 40); + label5.setText("User defined 2 state button"); + label5.setTextBold(); + label5.setOpaque(true); +} + diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/data/bulb.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/data/bulb.png new file mode 100644 index 0000000..75a43c2 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/data/bulb.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/data/s2.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/data/s2.png new file mode 100644 index 0000000..b96a33e Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/data/s2.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/data/s3.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/data/s3.png new file mode 100644 index 0000000..f328950 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_ImgTogButton/data/s3.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Knob_Config/G4P_Knob_Config.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Knob_Config/G4P_Knob_Config.pde new file mode 100644 index 0000000..fd36efc --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Knob_Config/G4P_Knob_Config.pde @@ -0,0 +1,127 @@ +/** + * This program demonstrates the various configuration options available + * for the knob control (GKnob). + * + * The only thing not set is the range limits. By default the knob returns + * vales in the range 0.0 to 1.0 inclusive. Use setLimits() to set your + * own range. + * + * (c) 2013 Peter Lager + * + */ + +import g4p_controls.*; + +GKnob kb; +int bgcol = 128; + +public void setup() { + size(500, 360); + G4P.setCursorOff(CROSS); + // Create the knob using default settings + kb = new GKnob(this, 60, 60, 180, 180, 0.8f); + // Now make the configuration controls + makeKnobConfigControls(); +} + +public void draw() { + background(bgcol); + fill(227, 230, 255); + noStroke(); + rect(width - 190, 0, 200, height); + rect(0, height - 84, width - 190, 84); +} + +public void handleKnobEvents(GValueControl knob, GEvent event) { + // Has the demo knob turned? + if (kb == knob) + lblValue.setText("" + knob.getValueS()); + // These 2 knobs are part to configure the demo + else if (knbStart == knob || knbEnd == knob) + this.kb.setTurnRange(knbStart.getValueF(), knbEnd.getValueF()); +} + + +public void handleButtonEvents(GButton button, GEvent event) { + if (button.tagNo >= 1000) { + kb.setLocalColorScheme(button.tagNo - 1000); + lblValue.setLocalColorScheme(button.tagNo - 1000); + } + else if (btnMakeCode == button) + placeCodeOnClipboard(); +} + +public void handleSliderEvents(GValueControl slider, GEvent event) { + if (slider == sdrSense) + kb.setSensitivity(slider.getValueF()); + else if (slider == sdrSense) + kb.setSensitivity(slider.getValueF()); + else if (slider == sdrEasing) + kb.setEasing(slider.getValueF()); + else if (slider == sdrNbrTicks) + kb.setNbrTicks(slider.getValueI()); + else if (slider == sdrBack) + bgcol = slider.getValueI(); + else if (slider == sdrGripRatio) + kb.setGripAmount(slider.getValueF()); +} + +public void handleToggleControlEvents(GToggleControl option, GEvent event) { + if (option == optAngular) + kb.setTurnMode(G4P.CTRL_ANGULAR); + else if (option == optXdrag) + kb.setTurnMode(G4P.CTRL_HORIZONTAL); + else if (option == optYdrag) + kb.setTurnMode(G4P.CTRL_VERTICAL); + else if (option == cbxOverArc) + kb.setOverArcOnly(option.isSelected()); + else if (option == cbxOverAll) + kb.setIncludeOverBezel(option.isSelected()); + else if (option == cbxOpaque) + kb.setOpaque(option.isSelected()); + else if (option == cbxShowArcOnly) + kb.setShowArcOnly(option.isSelected()); + else if (option == cbxShowTicks) + kb.setShowTicks(option.isSelected()); + else if (option == cbxShowTrack) + kb.setShowTrack(option.isSelected()); + else if (option == cbxSticky) + kb.setStickToTicks(option.isSelected()); +} + + +private void placeCodeOnClipboard() { + StringBuilder s = new StringBuilder(); + s.append("// Generated by the GKnob example program\n\n"); + s.append("import g4p_controls.*;\n\n"); + s.append("GKnob kb; \n\n"); + s.append("void setup() { \n"); + s.append(" size(300, 300); \n"); + s.append(" kb = new GKnob(this, 60, 60, 180, 180, " + sdrGripRatio.getValueF() + "); \n"); + s.append(" // Some of the following statements are not actually\n"); + s.append(" // required because they are setting the default value. \n"); + s.append(" kb.setTurnRange("+knbStart.getValueF()+", " + knbEnd.getValueF() + "); \n"); + s.append(" kb.setLocalColorScheme(" + kb.getLocalColorScheme() + "); \n"); + s.append(" kb.setOpaque(" + kb.isOpaque() + "); \n"); + s.append(" kb.setValue(" + kb.getValueF() + "); \n"); + s.append(" kb.setNbrTicks(" + kb.getNbrTicks() + "); \n"); + s.append(" kb.setShowTicks(" + kb.isShowTicks() + "); \n"); + s.append(" kb.setShowTrack(" + kb.isShowTrack() + "); \n"); + s.append(" kb.setShowArcOnly(" + kb.isShowArcOnly() +"); \n"); + s.append(" kb.setStickToTicks(" + kb.isStickToTicks() + "); \n"); + s.append(" kb.setTurnMode(" + kb.getTurnMode() + "); \n"); + s.append(" kb.setIncludeOverBezel(" + kb.isIncludeOverBezel() + "); \n"); + s.append(" kb.setOverArcOnly(" + kb.isOverArcOnly() + "); \n"); + s.append(" kb.setSensitivity(" + kb.getSensitivity() + "); \n"); + s.append(" kb.setEasing(" + kb.getEasing() + "); \n"); + s.append("} \n\n"); + s.append("void draw(){ \n"); + s.append(" background(" + bgcol + "); \n"); + s.append("} \n"); + if (GClip.copy(s.toString())) + println("Paste code into empty Processing sketch"); + else + System.err.println("UNABLE TO ACCESS CLIPBOARD"); +} + + diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Knob_Config/configcontrols.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Knob_Config/configcontrols.pde new file mode 100644 index 0000000..302cf23 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Knob_Config/configcontrols.pde @@ -0,0 +1,107 @@ +// The code in this tab is used to create all the other +// controls needed to configure the knob control. + +GToggleGroup tg = new GToggleGroup(); +GOption optAngular, optXdrag, optYdrag; +GCheckbox cbxOpaque, cbxOverArc, cbxOverAll; +GCheckbox cbxShowTicks, cbxShowArcOnly, cbxShowTrack; +GCheckbox cbxSticky; +GSlider sdrBack, sdrSense, sdrNbrTicks, sdrEasing, sdrGripRatio; +GLabel lblMC, lblVisible, lblSense, lblNbrTicks; +GLabel lblEasing, lblValue, lblAngles, lblGripAmount; +GKnob knbStart, knbEnd; +GButton btnMakeCode; +GButton[] btnColours = new GButton[8]; + +public void makeKnobConfigControls() { + // Create colour scheme selectors + int x = width - 42, y = 2; + for (int i = 0; i < btnColours.length; i++) { + btnColours[i] = new GButton(this, x, y + i * 20, 40, 18, "" + (i+1)); + btnColours[i].tag = "Button: " + (i+1); + btnColours[i].setLocalColorScheme(i); + btnColours[i].tagNo = 1000+i; + } + // Create sliders + x = width-100; + y = 162; + bgcol = 200; + sdrBack = new GSlider(this, x, y, 162, 80, 12); + sdrBack.setLimits(bgcol, 0, 255); + sdrBack.setRotation(-PI/2); + sdrBack.setTextOrientation(G4P.ORIENT_RIGHT); + sdrBack.setEasing(20); + sdrBack.setShowValue(true); + sdrBack.setShowTicks(true); + + x = width - 180; + y = 250; + sdrSense = new GSlider(this, x, y, 80, 40, 12); + sdrSense.setLimits(1.0f, 0.2f, 5.0f); + sdrSense.setShowValue(true); + lblSense = new GLabel(this, x + 82, y, 100, 40, "XY drag rate"); + lblSense.setTextAlign(GAlign.LEFT, null); + lblSense.setTextItalic(); + + sdrEasing = new GSlider(this, x, y + 34, 80, 40, 12); + sdrEasing.setLimits(1.0f, 1.0f, 30.0f); + sdrEasing.setShowValue(true); + lblEasing = new GLabel(this, x + 82, y + 34, 80, 40, "Easing"); + lblEasing.setTextAlign(GAlign.LEFT, null); + lblEasing.setTextItalic(); + + sdrNbrTicks = new GSlider(this, x, y + 68, 80, 40, 12); + sdrNbrTicks.setLimits(2, 2, 15); + sdrNbrTicks.setShowValue(true); + lblNbrTicks = new GLabel(this, x + 82, y + 68, 80, 40, "No. of ticks"); + lblNbrTicks.setTextAlign(GAlign.LEFT, null); + lblNbrTicks.setTextItalic(); + + x = width - 180; + y = 164; + lblMC = new GLabel(this, x, y, 178, 18, "Knob turning control"); + lblMC.setTextAlign(GAlign.LEFT, null); + lblMC.setTextBold(); + optAngular = new GOption(this, x, y + 20, 80, 18, "Angular"); + optXdrag = new GOption(this, x, y + 40, 80, 18, "X drag"); + optYdrag = new GOption(this, x, y + 60, 80, 18, "Y drag"); + tg.addControls(optAngular, optXdrag, optYdrag); + optXdrag.setSelected(true); + cbxOverArc = new GCheckbox(this, x + 90, y + 20, 90, 18, "Arc only"); + cbxOverAll = new GCheckbox(this, x + 90, y + 40, 90, 18, "Incl. Bezel"); + + x = width - 180; + y = 2; + lblVisible = new GLabel(this, x, y, 70, 18, "Visible"); + lblVisible.setTextBold(); + cbxShowTicks = new GCheckbox(this, x, y + 20, 70, 18, "Ticks"); + cbxShowTicks.setSelected(true); + cbxShowTrack = new GCheckbox(this, x, y + 40, 70, 18, "Track"); + cbxShowTrack.setSelected(true); + cbxShowArcOnly = new GCheckbox(this, x, y + 60, 80, 18, "Arc Only"); + cbxOpaque = new GCheckbox(this, x, y + 80, 70, 18, "Opaque"); + cbxSticky = new GCheckbox(this, x, y + 110, 70, 40, "Stick to ticks"); + + x = (int) (kb.getCX() - 40); + y = (int) (kb.getY() -40); + lblValue = new GLabel(this, x, y, 80, 30); + lblValue.setOpaque(true); + lblValue.setText("" + kb.getValueS()); + + x = 5; + y = height - 80; + knbStart = new GKnob(this, x, y, 50, 50, 1); + knbStart.setTurnRange(0, 360); + knbStart.setLimits(110, 0, 360); + knbStart.setTurnMode(G4P.CTRL_ANGULAR); + knbEnd = new GKnob(this, x + 50, y, 50, 50, 1); + knbEnd.setTurnRange(0, 360); + knbEnd.setLimits(70, 0, 360); + knbEnd.setTurnMode(G4P.CTRL_ANGULAR); + lblAngles = new GLabel(this, 0, y + 55, 110, 20, "Turn Limits"); + sdrGripRatio = new GSlider(this, x + 110, y, 120, 40, 12); + sdrGripRatio.setShowDecor(false, false, true, false); + sdrGripRatio.setValue(0.8f); + lblGripAmount = new GLabel(this, x + 226, y, 80, 40, "Grip Ratio"); + btnMakeCode = new GButton(this, x + 110, y + 36, 182, 38, "Place code for existing configuration on clipboard"); +} \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Mandelbrot/G4P_Mandelbrot.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Mandelbrot/G4P_Mandelbrot.pde new file mode 100644 index 0000000..f70619c --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Mandelbrot/G4P_Mandelbrot.pde @@ -0,0 +1,316 @@ +/** + * The most complicated example so far. This will display the Mandelbrot + * set in a separate window. Draw a rectangle over the image to create + * a new Mandelbrot in a new window ... + * + * (c) 2012 by Peter Lager + */ + +import g4p_controls.*; +import java.awt.Color; + +final int MAX_ITERATE = 128; + +final int MAX_WIDTH = 400; +final int MAX_HEIGHT = 400; + +final int MIN_WIDTH = 30; +final int MIN_HEIGHT = 30; + +int[] colors = new int[MAX_ITERATE]; + +GButton btnStart; +GLabel label; + +int locX = 100, locY = 100; + +String t0, t1, t2; + + +public void setup() { + size(300, 160); + G4P.setGlobalColorScheme(G4P.ORANGE_SCHEME); + btnStart = new GButton(this, (width - 80)/2, height - 30, 80, 20, "START"); + t0 = "MANDELBROT GALORE\n"; + t1 = "Click on the START button to see the Mandelbrot set in a separate window. "; + t2 = " To zoom into the set drag a box on the area you are interested in and "; + t2 += "he enlarged image will appear in a new window."; + label = new GLabel(this, 20, 20, 260, 200, t0 + t1 + t2); + label.setTextAlign(GAlign.CENTER, GAlign.TOP); + createColours(); +} + +/** + * Draw for the main sketch window + */ +public void draw() { + background(230, 230, 230); +} + +/** + Create the colours to be used. + */ +public void createColours() { + colors[MAX_ITERATE-1] = color(0); + float hue = 0.0f; + float bright = 1.0f; + float binc = 0.009f/MAX_ITERATE; + for (int i = 0; i < MAX_ITERATE - 2; i++) { + colors[i] = Color.HSBtoRGB(hue, 1.0f, bright); + hue = (hue + 0.1309f) % 1.0f ; + bright -= binc; + } +} + +/** + * Create a PImage of the correct size and using the data + * for a window create the Mandelbrot image. Signal when + * complete by setting imgDone = true + * + * @param data + */ +public void calcMandlebrot(MyWinData data) { + double x0, x1, y0, y1, deltaX, deltaY; + double colX, rowY; + x0 = data.sx; + x1 = data.ex; + y0 = data.sy; + y1 = data.ey; + deltaX = (x1 - x0)/data.w; + deltaY = (y1 - y0)/data.h; + data.img.loadPixels(); + int count = 0; + Complex c = new Complex(); + Complex z = new Complex(); + + colX = x0; + rowY = y0; + int minC = 999, maxC = -999; + for (int row = 0; row < data.h; row++) { + colX = x0; + for (int col = 0; col < data.w; col++) { + count = 0; + c.set(colX, rowY); + z.set(colX, rowY); + while (count < MAX_ITERATE-1 && z.sizeSquared () < 4.0) { + z = z.squared().add(c); + count++; + } + if (count < minC) minC = count; + if (count > maxC) maxC = count; + data.img.pixels[col + row * data.w] = colors[count]; + colX += deltaX; + } + rowY += deltaY; + } + data.img.updatePixels(); + data.imgDone = true; +} + +/** + * Create and display a new Frame for a Mandelbrot + * make it as big as possible to fit MAX_WIDTH and MAX_HEIGHT + * but reject if this results in the width or height being less + * than the minimum vales (MIN_WIDTH, MIN_HEIGHT. + * + * @param nsx + * @param nex + * @param nsy + * @param ney + * @return + */ +public boolean makeNewBrotWindow(double nsx, double nex, double nsy, double ney, int action) { + MyWinData mydata = new MyWinData(); + GWindow window = null; + // Using doubles for greater magnification range + double mag = 2.5 / (nex-nsx); + String s = String.format("%.2E", mag); + String title = "Mandelbrot (x "+s+")"; + float ratio = (float) ((nex-nsx)/(ney-nsy)); + if (ratio > MAX_WIDTH/MAX_HEIGHT) { + mydata.w = MAX_WIDTH; + mydata.h = (int)( MAX_HEIGHT / ratio); + } + else { + mydata.h = MAX_HEIGHT; + mydata.w = (int)( MAX_WIDTH * ratio); + } + mydata.sx = nsx; + mydata.sy = nsy; + mydata.ex = nex; + mydata.ey = ney; + mydata.imgDone = false; + + if (mydata.w >= MIN_WIDTH && mydata.h >= MIN_HEIGHT) { + mydata.img = new PImage(mydata.w, mydata.h); + window = new GWindow(this, title, locX, locY, mydata.w, mydata.h, false, null); + // Need to replace with screen.width and screen.height for + // Processing 1.5.1 + locX = (locX + mydata.w + 20)%(displayWidth - MAX_WIDTH); + locY = (locY + 20)%(displayHeight - MAX_HEIGHT); + window.addData(mydata); + window.addDrawHandler(this, "windowDraw"); + window.addMouseHandler(this, "windowMouse"); + window.setActionOnClose(action); + calcMandlebrot(mydata); + return true; + } + return false; +} + + +/** + * Click the button to create the windows. + * @param button + */ +public void handleButtonEvents(GButton button, GEvent event) { + if (btnStart == button) { + makeNewBrotWindow(-2.0f, 0.5f, -1.25f, 1.25f, GWindow.KEEP_OPEN); + btnStart.setVisible(false); + label.setText(t0 + t2); + } +} + +/** + * Handles mouse events for ALL GWindow objects + * + * @param appc the PApplet object embeded into the frame + * @param data the data for the GWindow being used + * @param event the mouse event + */ +public void windowMouse(GWinApplet appc, GWinData data, MouseEvent event) { + MyWinData d = (MyWinData)data; + if (d.imgDone == false) + return; + switch(event.getAction()) { + case MouseEvent.PRESS: + d.msx = d.mex = appc.mouseX; + d.msy = d.mey = appc.mouseY; + appc.loop(); + appc.frameRate(30); + break; + case MouseEvent.RELEASE: + d.mex = appc.mouseX; + d.mey = appc.mouseY; + // Make sure the coordinates are top left / bottom left + int temp; + if (d.msx > d.mex) { + temp = d.msx; + d.msx = d.mex; + d.mex = temp; + } + if (d.msy > d.mey) { + temp = d.msy; + d.msy = d.mey; + d.mey = temp; + } + // Calculate the new Mandelbrot plane coordinates + double nsx, nex, nsy, ney; + nsx = dmap((double)d.msx, (double)0, (double)d.w, d.sx, d.ex); + nex = dmap((double)d.mex, (double)0, (double)d.w, d.sx, d.ex); + nsy = dmap((double)d.msy, (double)0, (double)d.h, d.sy, d.ey); + ney = dmap((double)d.mey, (double)0, (double)d.h, d.sy, d.ey); + makeNewBrotWindow(nsx, nex, nsy, ney, GWindow.CLOSE_WINDOW); + d.msx = d.mex = d.msy = d.mey = 0; + appc.noLoop(); + break; + case MouseEvent.DRAG: + d.mex = appc.mouseX; + d.mey = appc.mouseY; + break; + } +} + +/** + * Copied from PApplet and converted floats to doubles + * @param value + * @param istart + * @param istop + * @param ostart + * @param ostop + * @return + */ +double dmap(double value, double istart, double istop, double ostart, double ostop) { + return ostart + (ostop - ostart) * ((value - istart) / (istop - istart)); +} + +/** + * Handles drawing to the windows PApplet area + * + * @param appc the PApplet object embeded into the frame + * @param data the data for the GWindow being used + */ +public void windowDraw(GWinApplet appc, GWinData data) { + MyWinData d = (MyWinData)data; + if (d.imgDone) { + appc.image(d.img, 0, 0); + if (d.msx != d.mex || d.msy != d.mey) { + appc.strokeWeight(2); + appc.stroke(255); + appc.noFill(); + appc.rectMode(CORNERS); + appc.rect(d.msx, d.msy, d.mex, d.mey); + } + } +} + +/** + A basic complex number class. + */ +class Complex { + public double real; + public double img; + + Complex() { + real = img = 0.0f; + } + + public Complex(float r, float i) { + super(); + this.real = r; + this.img = i; + } + + public void set(double colX, double rowY) { + real = colX; + img = rowY; + } + + public Complex add(Complex c) { + real += c.real; + img += c.img; + return this; + } + + public void mult(Complex c) { + double nReal = real * c.real - img * c.img; + double nImg = real * c.img + img * c.real; + real = nReal; + img = nImg; + } + + public Complex squared() { + double nReal = (real - img)*(real + img); + double nImg = 2 * real * img; + real = nReal; + img = nImg; + return this; + } + + public double sizeSquared() { + return real * real + img * img; + } +} + +/** + * Simple class that extends GWinData and holds the data that is specific + * to a particular window. + */ +class MyWinData extends GWinData { + + public int msx, msy, mex, mey; + public double sx, sy, ex, ey; + public int w, h; + public PImage img; + public boolean imgDone; +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_RGBmixer/G4P_RGBmixer.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_RGBmixer/G4P_RGBmixer.pde new file mode 100644 index 0000000..2048774 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_RGBmixer/G4P_RGBmixer.pde @@ -0,0 +1,74 @@ +/** + Demonstration of the GKnob component which is part of + the G4P (GUI for Processing) library. + + Simple program that creates 3 knobs but only shows a third + of each one. They are used to control the RGB balance for + the rectangle fill. + + (c)2012 Peter Lager + */ + +import g4p_controls.*; + +GKnob knbRed, knbGreen, knbBlue; +GLabel label; + +int r, g, b; +int kx, ky; + +void setup() { + size(400, 220); + r = g = b = 160; + kx = 20; + ky = 20; + + label = new GLabel(this, 0, height - 26, width, 26, + "Drag mouse in a circular movement around the knobs centre"); + label.setOpaque(true); + label.setLocalColorScheme(G4P.CYAN_SCHEME); + + knbRed = new GKnob(this, kx, ky, 150, 150, 0.8); + knbRed.setTurnRange(150, 270); + knbRed.setTurnMode(G4P.CTRL_ANGULAR); + knbRed.setArcPolicy(true, true, true); + knbRed.setLimits(r, 0, 255); + knbRed.setNbrTicks(9); + knbRed.setLocalColorScheme(G4P.RED_SCHEME); + + knbGreen = new GKnob(this, kx + 8, ky, 150, 150, 0.8); + knbGreen.setTurnRange(270, 30); + knbGreen.setTurnMode(G4P.CTRL_ANGULAR); + knbGreen.setArcPolicy(true, true, true); + knbGreen.setLimits(g, 0, 255); + knbGreen.setNbrTicks(9); + knbGreen.setLocalColorScheme(G4P.GREEN_SCHEME); + + knbBlue = new GKnob(this, kx + 4, ky + 9, 150, 150, 0.8); + knbBlue.setTurnRange(30, 150); + knbBlue.setTurnMode(G4P.CTRL_ANGULAR); + knbBlue.setArcPolicy(true, true, true); + knbBlue.setLimits(b, 0, 255); + knbBlue.setNbrTicks(9); + knbBlue.setLocalColorScheme(G4P.BLUE_SCHEME); + + stroke(0); + strokeWeight(3); + rectMode(CORNERS); +} + +void draw() { + background(220, 220, 255); + // Color sampler + fill(r, g, b); + rect(kx + 190, 40, width - 40, height - 40); +} + +void handleKnobEvents(GValueControl knob, GEvent event) { + if (knob == knbRed) + r = knob.getValueI(); + else if (knob == knbGreen) + g = knob.getValueI(); + else if (knob == knbBlue) + b = knob.getValueI(); +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/G4P_Showcase.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/G4P_Showcase.pde new file mode 100644 index 0000000..f58f37c --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/G4P_Showcase.pde @@ -0,0 +1,163 @@ +/** + This example showcases many of the controls available. Click + on the 'G4P config window' button to open the control window + for more options. + + Some of the other examples demonstrate individual controls + in more detail. + + (c) 2013 Peter Lager + */ + +import g4p_controls.*; + + +// G4P components for main window +GPanel pnlControls; +GLabel lblAction; +GTextField txfDemo; +GTextArea txaDemo; +GKnob knbDemo; +GTimer tmrTimer; +GButton btnTimer; +GTabManager tt; +GCheckbox cbxSticky; +GButton btnControl; // Used to start controller window +GStick astick; +String startText; +PImage imgBgImage; + +int count = 0; +GLabel lblMC; +GOption optAngular, optYdrag, optXdrag; +GToggleGroup tg; +GSketchPad spad; +PGraphics pg; + +public void setup() { + size(600, 440); + // Load the background image + imgBgImage = loadImage("castle.jpg"); + makeDemoControls(); +} + +public void updateGraphic(int n) { + n = (n < 0) ? 0 : n % 30; + pg.beginDraw(); + pg.background(0); + pg.noStroke(); + pg.fill(255); + pg.ellipseMode(CENTER); + if (n >= 0) { + for (int i = 0; i < n; i++) { + int r = i / 10; + int c = i % 10; + pg.ellipse(5 + c * 10, 7 + r * 15, 6, 10); + } + } + pg.endDraw(); +} + +public void draw() { + background(imgBgImage); + updateGraphic(count); +} + +public void handleTextEvents(GEditableTextControl textcontrol, GEvent event) { + if (textcontrol == txaDemo) + lblAction.setText("TextArea: " + event); + if (textcontrol == txfDemo) + lblAction.setText("TextField: " + event); +} + +public void handlePanelEvents(GPanel panel, GEvent event) { + lblAction.setText("Panel: " + event); + if (event == GEvent.DRAGGED && sdrPanelPos != null) { + sdrPanelPos.setValueX(pnlControls.getX()); + sdrPanelPos.setValueY(pnlControls.getY()); + } +} + +public void handleSliderEvents(GValueControl slider, GEvent event) { + if (slider == sdrAlpha) { + int alpha = sdrAlpha.getValueI(); + if (alpha >= G4P.ALPHA_BLOCK) + slider.setLocalColorScheme(G4P.BLUE_SCHEME); + else + slider.setLocalColorScheme(G4P.RED_SCHEME); + G4P.setWindowAlpha(this, alpha); + } +} + +public void handleToggleControlEvents(GToggleControl option, GEvent event) { + if (option == optAngular) { + lblAction.setText("Knob is now using angular drag for rotation"); + knbDemo.setTurnMode(G4P.CTRL_ANGULAR); + } + if (option == optXdrag) { + lblAction.setText("Knob is now using horizontal drag for rotation"); + knbDemo.setTurnMode(G4P.CTRL_HORIZONTAL); + } + if (option == optYdrag) { + lblAction.setText("Knob is now using vertical drag for rotation"); + knbDemo.setTurnMode(G4P.CTRL_VERTICAL); + } + if (option == cbxSticky) { + lblAction.setText("Stick to ticks option changed"); + knbDemo.setStickToTicks(cbxSticky.isSelected()); + } +} + +public void handleStickEvents(GStick stick, GEvent event) { + String pos = ""; + // 4-way stick so forget 1,3,5 & 7 positions + switch(stick.getPosition()) { + case 0: + pos = "RIGHT"; + break; + case 2: + pos = "DOWN"; + break; + case 4: + pos = "LEFT"; + break; + case 6: + pos = "UP"; + break; + } + lblAction.setText("Stick control is in " + pos + " position"); +} + +public void handleSlider2DEvents(GSlider2D slider2d, GEvent event) { + pnlControls.moveTo(slider2d.getValueXI(), slider2d.getValueYI()); +} + +public void handleKnobEvents(GValueControl knob, GEvent event) { + if (knob == knbDemo) + lblAction.setText("Knob value is now " + knbDemo.getValueS()); + if (knob == knbAngle) { + pnlControls.setRotation(knbAngle.getValueF(), GControlMode.CENTER); + } +} + +public void handleButtonEvents(GButton button, GEvent event) { + // Create the control window? + if (button == btnControl && event == GEvent.CLICKED) { + lblAction.setText("Open control window and disable the button"); + createControlWindow(); + btnControl.setEnabled(false); + } + // Change the colour scheme + if (button.tagNo >= 1000 && event == GEvent.CLICKED) + G4P.setWindowColorScheme(this, button.tagNo - 1000); + if (button == btnTimer && event == GEvent.CLICKED) { + if (tmrTimer.isRunning()) + tmrTimer.stop(); + else + tmrTimer.start(); + } +} + +public void myTimerFunction(GTimer timer) { + count++; +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/cinfigwindowcontrols.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/cinfigwindowcontrols.pde new file mode 100644 index 0000000..e5fba25 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/cinfigwindowcontrols.pde @@ -0,0 +1,61 @@ +// ============================================= +// Create the configuration window and controls +// ============================================= + +GWindow windControl; +GButton[] btnColours = new GButton[8]; +GSlider sdrAlpha; +GKnob knbAngle; +GLabel lblAlpha1, lblAlpha2; +GSlider2D sdrPanelPos; + +public void createControlWindow() { + windControl = new GWindow(this, "Controls", 600, 400, 400, 300, false, JAVA2D); + PApplet app = windControl.papplet; // save some typing + // Create colour scheme selectors + int x = 8; + int y = 50; // app.height - 24; + for (int i = 0; i < btnColours.length; i++) { + btnColours[i] = new GButton(app, x + i * 34, y, 30, 18, "" + (i+1)); + btnColours[i].tag = "Button: " + (i+1); + btnColours[i].setLocalColorScheme(i); + btnColours[i].tagNo = 1000+i; + } + y = 0; //app.height - 90; + sdrAlpha = new GSlider(app, x, y, 268, 60, 12); + sdrAlpha.setLimits(255, 0, 255); +// sdrAlpha.setRotation(-PI/2); +// sdrAlpha.setTextOrientation(G4P.ORIENT_RIGHT); + sdrAlpha.setEasing(20); + sdrAlpha.setShowValue(true); + sdrAlpha.setShowTicks(true); + + x = 290; + y = 16; + lblAlpha1 = new GLabel(app, x, y, 120, 26, "Alpha Slider"); + lblAlpha1.setTextBold(); + lblAlpha2 = new GLabel(app, x, y + 30, 110, 80, "When alpha falls below " + G4P.ALPHA_BLOCK + " it will disable the controls in the main sketch."); + + x = app.width - 120; + y = app.height - 120; + knbAngle = new GKnob(app, x, y, 100, 100, 0.75f); + knbAngle.setTurnRange(0, 360); + knbAngle.setLimits(0.0f, 0.0f, TWO_PI); + knbAngle.setTurnMode(G4P.CTRL_ANGULAR); + knbAngle.setIncludeOverBezel(true); + knbAngle.setNbrTicks(13); + knbAngle.setStickToTicks(true); + + sdrPanelPos = new GSlider2D(app, 40, 84, 200, 200); + sdrPanelPos.setLimitsX(pnlControls.getX(), 0, 200); + sdrPanelPos.setLimitsY(pnlControls.getY(), 0, 200); + + windControl.addDrawHandler(this, "drawController"); +} + +/* + * The draw handler for the control window + */ +public void drawController(GWinApplet appc, GWinData data) { + appc.background(227, 230, 255); +} \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/data/book.txt b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/data/book.txt new file mode 100644 index 0000000..a5acc27 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/data/book.txt @@ -0,0 +1 @@ +Game AI has undergone a quiet revolution in the past few years. No longer is it something that most developers consider only towards the end of a project when shipping deadlines loom and the publisher is pushing to have the game ship before the next holiday milestone. Now game AI is something that is planned for, soemthing that developers are deliberately making as important a part of a game's development as the graphics or the sound effects. The market is rife with games of all kinds and developers are looking for every edge they can get to get the game noticed. \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/data/castle.jpg b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/data/castle.jpg new file mode 100644 index 0000000..9d42b35 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/data/castle.jpg differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/data/time.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/data/time.png new file mode 100644 index 0000000..a07af7f Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/data/time.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/showcasecontrols.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/showcasecontrols.pde new file mode 100644 index 0000000..a80780b --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Showcase/showcasecontrols.pde @@ -0,0 +1,82 @@ +// ============================================ +// Create the showcase controls +// ============================================ + +public void makeDemoControls() { + // Create the panel of this size + int pX = 10, pY = 30, pHeight = 240, pWidth = 400; + pnlControls = new GPanel(this, pX, pY, pWidth, pHeight, "Panel Tab Text (drag to move : click to open/close)"); + pnlControls.setOpaque(true); + pnlControls.setCollapsed(false); + // Create the user action feedback label + lblAction = new GLabel(this, 0, pHeight-20, pWidth, 20, "This is a GLabel control - for status info"); + lblAction.setOpaque(true); + pnlControls.addControl(lblAction); + + //Load some sample text + String[] paragraphs = loadStrings("book.txt"); + startText = PApplet.join(paragraphs, '\n'); + + // Create a text area with both horizontal and + // vertical scrollbars that automatically hide + // when not needed. + txaDemo = new GTextArea(this, 4, 24, 220, 96, G4P.SCROLLBARS_BOTH | G4P.SCROLLBARS_AUTOHIDE); + txaDemo.setText(startText, 250); + txaDemo.setDefaultText("Please enter some text"); + pnlControls.addControl(txaDemo); + // Create a text field with horizontal scrollbar + txfDemo = new GTextField(this, 4, 126, 220, 30, G4P.SCROLLBAR_HORIZONTAL | G4P.SCROLLBARS_AUTOHIDE); + txfDemo.setDefaultText("This is default text"); + pnlControls.addControl(txfDemo); + // Add tab controls + tt = new GTabManager(); + tt.addControls(txaDemo, txfDemo); + + // Checkbox + cbxSticky = new GCheckbox(this, 230, 130, 120, 20, "Stick to ticks"); + pnlControls.addControl(cbxSticky); + + // Knob + knbDemo = new GKnob(this, 230, 30, 90, 90, 0.76f); + knbDemo.setLimits(0.0f, 0.0f, TWO_PI); + knbDemo.setNbrTicks(10); + knbDemo.setEasing(5); + knbDemo.setShowArcOnly(true); + knbDemo.setOverArcOnly(true); + knbDemo.setSensitivity(2.0f); + pnlControls.addControl(knbDemo); + + lblMC = new GLabel(this, 310, 22, 100, 50, "Knob turning control"); + lblMC.setTextAlign(GAlign.LEFT, null); + lblMC.setTextAlign(GAlign.CENTER, GAlign.TOP); + lblMC.setTextBold(); + optAngular = new GOption(this, 326, 60, 80, 18, "Angular"); + optXdrag = new GOption(this, 326, 80, 80, 18, "X drag"); + optYdrag = new GOption(this, 326, 100, 80, 18, "Y drag"); + tg = new GToggleGroup(); + tg.addControls(optAngular, optXdrag, optYdrag); + optXdrag.setSelected(true); + pnlControls.addControls(lblMC, optAngular, optXdrag, optYdrag); + + // Create timer button + btnTimer = new GButton(this, pWidth-100, pHeight-60, 100, 40, "Start"); + btnTimer.setIcon("time.png", 1, null, null); + tmrTimer = new GTimer(this, this, "myTimerFunction", 300); + pnlControls.addControl(btnTimer); + // When the timer is on we will see it in a GSketchPad control + // Create the GSkethPad control to position and display the graphic + spad = new GSketchPad(this, pWidth-210, pHeight-60, 100, 40); + // Now create and clear graphic to be used (JAVA parameter + // parameter is only needed for Processing 1.5.1) + pg = createGraphics(100, 46, JAVA2D); + // Add the graphic to the control. + spad.setGraphic(pg); + updateGraphic(3); // Method for drawing the graphic + pnlControls.addControl(spad); + // 4-way stick control + astick = new GStick(this, pWidth - 290, pHeight - 80, 60, 60); + pnlControls.addControl(astick); + // Create button to open the control window + btnControl = new GButton(this, 2, pHeight - 60, 100, 40, "G4P config window"); + pnlControls.addControl(btnControl); +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Sketchpad/G4P_Sketchpad.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Sketchpad/G4P_Sketchpad.pde new file mode 100644 index 0000000..8cc975a --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Sketchpad/G4P_Sketchpad.pde @@ -0,0 +1,65 @@ +import g4p_controls.*; + +GPanel pnl; +GSketchPad spad; +GButton btnClear; +PGraphics pg; + +void setup() { + size(400, 300); + // Now create and clear graphic to be used (JAVA parameter + // parameter is only needed for Processing 1.5.1) + pg = createGraphics(200, 160, JAVA2D); + clearGraphic(); + // Create the G4P control to position and display the graphic + spad = new GSketchPad(this, 0, 0, 100, 80); + // Set the graphic for this control. + // The graphic will be scaled to fit the control. + spad.setGraphic(pg); + // Create the button to clear the graphic + btnClear = new GButton(this, 0, 0, 80, 20, "Clear"); + // Create the panel and add the controls + pnl = new GPanel(this, 20, 30, 160, 140, "Sketch Test"); + pnl.addControl(btnClear, 10, 24); + pnl.addControl(spad, 10, 50); + // Expand the panel + pnl.setCollapsed(false); +} + +void draw() { + background(240); + // Every 10th frame update the sketchpad graphic + if (frameCount % 10 == 0) + updateGraphic(); +} + +void handleButtonEvents(GButton button, GEvent event) { + clearGraphic(); +} + +// Clear the sketchpad graphic +void clearGraphic() { + pg.beginDraw(); + pg.background(255, 255, 200); + pg.noFill(); + pg.ellipseMode(CORNERS); + pg.endDraw(); +} + +// Add a line or ellipse to the sketchpad graphic +void updateGraphic() { + float x0 = random(10, pg.width-10); + float x1 = random(10, pg.width-10); + float y0 = random(10, pg.height-10); + float y1 = random(10, pg.height-10); + int col = color(random(64, 255), random(64, 255), random(64, 255)); + pg.beginDraw(); + pg.stroke(col); + pg.strokeWeight(random(2, 5)); + if (random(0, 1) < 0.5f) + pg.line(x0, y0, x1, y1); + else + pg.ellipse(x0, y0, x1, y1); + pg.endDraw(); +} + diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/G4P_Slider2D.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/G4P_Slider2D.pde new file mode 100644 index 0000000..81a4fa5 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/G4P_Slider2D.pde @@ -0,0 +1,70 @@ +/** + Demonstration of the GSlider2D control to position a + gun-sight over a street scene. + + created by Peter Lager + + */ +import g4p_controls.*; + + +GSlider2D sdrSniper; + +PImage imgScene, imgSight; +// Lens properties +int lsize = 40, lsize2 = lsize * lsize; +float mag = 2.0f; +float k = -0.00002; + +int sightX, sightY; +int border, borderViaLens; + +public void setup() { + size(540, 460); + cursor(CROSS); + imageMode(CENTER); + imgScene = loadImage("backdrop.jpg"); + imgSight = loadImage("sight.png"); + + // border colours + borderViaLens = color(0, 80, 0); + sdrSniper = new GSlider2D(this, 180, 340, 180, 100); + sdrSniper.setLimitsX(180, 60, 480); + sdrSniper.setLimitsY(150, 60, 280); + sdrSniper.setEasing(8); + // sdrSniper.setOpaque(false); + sightX = sdrSniper.getValueXI(); + sightY = sdrSniper.getValueYI(); +} + +public void draw() { + background(imgScene); + showLens(sightX, sightY); + image(imgSight, sightX, sightY); +} + +public void handleSlider2DEvents(GSlider2D slider2d, GEvent event) { + if (slider2d == sdrSniper) { + sightX = sdrSniper.getValueXI(); + sightY = sdrSniper.getValueYI(); + } +} + +public void showLens(int x, int y) { + int u, v, r2; + float f; + for (int vd = - lsize; vd < lsize; vd++) { + for (int ud = - lsize; ud < lsize; ud++) { + r2 = ud*ud + vd*vd; + if (r2 <= lsize2) { + f = mag + k * r2; + u = floor(ud/f) + x; + v = floor(vd/f) + y; + if (u >= 0 && u < imgScene.width && v >=0 && v < imgScene.height) + set(ud + x, vd + y, imgScene.get(u, v)); + else + set(ud + x, vd + y, borderViaLens); + } + } + } +} \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/data/backdrop.jpg b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/data/backdrop.jpg new file mode 100644 index 0000000..e2963fa Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/data/backdrop.jpg differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/data/sight.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/data/sight.png new file mode 100644 index 0000000..6031f87 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/data/sight.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/data/street-scene.jpg b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/data/street-scene.jpg new file mode 100644 index 0000000..d739344 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/data/street-scene.jpg differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/sketch.properties b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/sketch.properties new file mode 100644 index 0000000..09d6f35 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider2D/sketch.properties @@ -0,0 +1,2 @@ +mode.id=processing.mode.java.JavaMode +mode=Java diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider_Config/G4P_Slider_Config.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider_Config/G4P_Slider_Config.pde new file mode 100644 index 0000000..e3a1d23 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider_Config/G4P_Slider_Config.pde @@ -0,0 +1,106 @@ +/** + * This program demonstrates the various configuration options + * available for the slider control (GSlider). + * + * The only thing not set is the range limits. By default the + * slider returns values in the range 0.0 to 1.0 inclusive. + * Use setLimits() to set your own range. + * + * @author Peter Lager + * + */ + +import g4p_controls.*; + +GSlider sdr; +int bgcol = 128; + +public void setup() { + size(500, 360); + G4P.setCursorOff(CROSS); + sdr = new GSlider(this, 55, 80, 200, 100, 15); + makeSliderConfigControls(); +} + +public void draw() { + background(bgcol); + fill(227, 230, 255); + noStroke(); + rect(width - 190, 0, 200, height); + rect(0, height - 84, width - 190, 84); +} + +public void handleSliderEvents(GValueControl slider, GEvent event) { + if (slider == sdr) // The slider being configured? + println(sdr.getValueS() + " " + event); + if (slider == sdrEasing) + sdr.setEasing(slider.getValueF()); + else if (slider == sdrNbrTicks) + sdr.setNbrTicks(slider.getValueI()); + else if (slider == sdrBack) + bgcol = slider.getValueI(); +} + +public void handleKnobEvents(GValueControl knob, GEvent event) { + if (knbAngle == knob) + this.sdr.setRotation(knbAngle.getValueF(), GControlMode.CENTER); +} + +public void handleButtonEvents(GButton button, GEvent event) { + if (button.tagNo >= 1000) + sdr.setLocalColorScheme(button.tagNo - 1000); + else if (btnMakeCode == button) + placeCodeOnClipboard(); +} + + +public void handleToggleControlEvents(GToggleControl option, GEvent event) { + if (option == optLeft) + sdr.setTextOrientation(G4P.ORIENT_LEFT); + else if (option == optRight) + sdr.setTextOrientation(G4P.ORIENT_RIGHT); + else if (option == optTrack) + sdr.setTextOrientation(G4P.ORIENT_TRACK); + else if (option == cbxOpaque) + sdr.setOpaque(option.isSelected()); + else if (option == cbxValue) + sdr.setShowValue(option.isSelected()); + else if (option == cbxShowTicks) + sdr.setShowTicks(option.isSelected()); + else if (option == cbxLimits) + sdr.setShowLimits(option.isSelected()); + else if (option == cbxSticky) + sdr.setStickToTicks(option.isSelected()); +} + + +private void placeCodeOnClipboard() { + StringBuilder s = new StringBuilder(); + s.append("// Generated by the GKnob example program\n\n"); + s.append("import g4p_controls.*;\n\n"); + s.append("GSlider sdr; \n\n"); + s.append("void setup() { \n"); + s.append(" size(300, 300); \n"); + s.append(" sdr = new GSlider(this, 55, 80, 200, 100, 15); \n"); + s.append(" // Some of the following statements are not actually\n"); + s.append(" // required because they are setting the default value. \n"); + s.append(" sdr.setLocalColorScheme(" + sdr.getLocalColorScheme() + "); \n"); + s.append(" sdr.setOpaque(" + sdr.isOpaque() + "); \n"); + s.append(" sdr.setValue(" + sdr.getValueF() + "); \n"); + s.append(" sdr.setNbrTicks(" + sdr.getNbrTicks() + "); \n"); + s.append(" sdr.setShowLimits(" + sdr.isShowLimits() + "); \n"); + s.append(" sdr.setShowValue(" + sdr.isShowValue() + "); \n"); + s.append(" sdr.setShowTicks(" + sdr.isShowTicks() + "); \n"); + s.append(" sdr.setStickToTicks(" + sdr.isStickToTicks() + "); \n"); + s.append(" sdr.setEasing(" + sdr.getEasing() + "); \n"); + s.append(" sdr.setRotation(" + knbAngle.getValueF() + ", GControlMode.CENTER); \n"); + s.append("} \n\n"); + s.append("void draw(){ \n"); + s.append(" background(" + bgcol + "); \n"); + s.append("} \n"); + if (GClip.copy(s.toString())) + println("Paste code into empty Processing sketch"); + else + System.err.println("UNABLE TO ACCESS CLIPBOARD"); +} + diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider_Config/configcontrols.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider_Config/configcontrols.pde new file mode 100644 index 0000000..00f3dae --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Slider_Config/configcontrols.pde @@ -0,0 +1,84 @@ +// The code in this tab is used to create all the other +// controls needed to configure the slider control. + +GToggleGroup tg = new GToggleGroup(); +GOption optLeft, optRight, optTrack; +GCheckbox cbxOpaque, cbxShowTicks, cbxValue, cbxLimits, cbxSticky; +GSlider sdrBack, sdrNbrTicks, sdrEasing; +GLabel lblOrient, lblVisible, lblNbrTicks, lblEasing; +GKnob knbAngle; +GButton btnMakeCode; +GButton[] btnColours = new GButton[8]; + +public void makeSliderConfigControls() { + // Create colour scheme selectors + int x = width - 42, y = 2; + for (int i = 0; i < btnColours.length; i++) { + btnColours[i] = new GButton(this, x, y + i * 20, 40, 18, "" + (i+1)); + btnColours[i].tag = "Button: " + (i+1); + btnColours[i].setLocalColorScheme(i); + btnColours[i].tagNo = 1000+i; + } + // Create sliders + x = width-100; + y = 162; + bgcol = 200; + sdrBack = new GSlider(this, x, y, 162, 80, 12); + sdrBack.setLimits(bgcol, 0, 255); + sdrBack.setRotation(-PI/2); + sdrBack.setTextOrientation(G4P.ORIENT_RIGHT); + sdrBack.setEasing(20); + sdrBack.setShowValue(true); + sdrBack.setShowTicks(true); + + x = width - 180; + y = 250; + + sdrEasing = new GSlider(this, x, y + 34, 80, 40, 12); + sdrEasing.setLimits(1.0f, 1.0f, 30.0f); + sdrEasing.setShowValue(true); + lblEasing = new GLabel(this, x + 82, y + 34, 80, 40, "Easing"); + lblEasing.setTextAlign(GAlign.LEFT, null); + lblEasing.setTextItalic(); + + sdrNbrTicks = new GSlider(this, x, y + 68, 80, 40, 12); + sdrNbrTicks.setLimits(2, 2, 15); + sdrNbrTicks.setShowValue(true); + lblNbrTicks = new GLabel(this, x + 82, y + 68, 80, 40, "No. of ticks"); + lblNbrTicks.setTextAlign(GAlign.LEFT, null); + lblNbrTicks.setTextItalic(); + + x = width - 180; + y = 164; + lblOrient = new GLabel(this, x, y, 178, 18, "Orient Text"); + lblOrient.setTextAlign(GAlign.LEFT, null); + lblOrient.setTextBold(); + optLeft = new GOption(this, x, y + 20, 80, 18, "Left"); + optRight = new GOption(this, x, y + 40, 80, 18, "Right"); + optTrack = new GOption(this, x, y + 60, 80, 18, "Track"); + tg.addControls(optLeft, optRight, optTrack); + optTrack.setSelected(true); + + x = width - 180; + y = 2; + lblVisible = new GLabel(this, x, y, 70, 18, "Visible"); + lblVisible.setTextBold(); + cbxShowTicks = new GCheckbox(this, x, y + 20, 70, 18, "Ticks"); + cbxLimits = new GCheckbox(this, x, y + 40, 70, 18, "Limits"); + cbxValue = new GCheckbox(this, x, y + 60, 70, 18, "Value"); + cbxOpaque = new GCheckbox(this, x, y + 80, 70, 18, "Opaque"); + cbxSticky = new GCheckbox(this, x, y + 110, 70, 40, "Stick to ticks"); + + x = 10; + y = height - 80; + knbAngle = new GKnob(this, x, y, 70, 70, 0.6f); + knbAngle.setTurnRange(0, 360); + knbAngle.setLimits(0.0f, 0.0f, TWO_PI); + knbAngle.setTurnMode(G4P.CTRL_ANGULAR); + knbAngle.setIncludeOverBezel(true); + knbAngle.setNbrTicks(13); + knbAngle.setStickToTicks(true); + + btnMakeCode = new GButton(this, x + 100, y + 20, 182, 38, "Place code for existing configuration on clipboard"); +} + diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Stick/G4P_Stick.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Stick/G4P_Stick.pde new file mode 100644 index 0000000..b45ae3c --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Stick/G4P_Stick.pde @@ -0,0 +1,62 @@ +/** + This example demonstrates the `GStick control. + + (c) 2013 Peter Lager + */ +import g4p_controls.*; + +GStick joystick; +int facing = 6; +int dirX, dirY; +float px, py; +float speed; + +public void setup() { + size(300, 300); + float ss = 80; + joystick = new GStick(this, width-ss, height-ss, ss, ss); + // Change from the default X4 mode (4 position) to the + // 8 position mode. + joystick.setMode(G4P.X8); + strokeWeight(1.5f); + px = width/2; + py = height/2; +} + +public void draw() { + background(255, 255, 230); + // Calculate current position of arrow + px = (px + dirX * speed + width) % width; + py = (py + dirY * speed + height) % height; + + // Draw arrow in current position and rotation + pushMatrix(); + translate(px, py); + rotate(facing * PI/4); + fill(255, 200, 200); + stroke(160, 32, 32); + beginShape(); + vertex(-20, -10); + vertex(-20, 10); + vertex(30, 0); + endShape(CLOSE); + fill(160, 32, 32); + noStroke(); + ellipse(-6, 0, 10, 6); + popMatrix(); +} + +public void handleStickEvents(GStick stick, GEvent event) { + if (joystick == stick) { + int pos = stick.getPosition(); + if (pos < 0) // Stick is in rest position? + speed = 0; + else { // The is not at + facing = pos; + dirX = stick.getStickX(); + dirY = stick.getStickY(); + speed = 0.8; + } + } +} + diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextAreaControl/G4P_TextAreaControl.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextAreaControl/G4P_TextAreaControl.pde new file mode 100644 index 0000000..82742e0 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextAreaControl/G4P_TextAreaControl.pde @@ -0,0 +1,90 @@ +/** + * Sketch to demonstrate the GTextArea class. + * + * This component allows the user to enter and edit text. + * + * In this demonstration the text width > the display width + * so it can show off its' horizontal scrollbar. + * + * The control has been setup with some default text and + * this becomes visible when the control is empty. + * + * The buttons on the side will change the font style + * for the selected text. If no text has been selected + * then the buttons are ignored. + + * Note that super/sub script may not be available on your + * system. + * + * @author Peter Lager + * + */ + +import g4p_controls.*; + + +GTextArea txaSample; +int bgcol = 32; +String startText; + +public void setup() { + size(700, 360); + + //Load some sample text + String[] paragraphs = loadStrings("book3.txt"); + startText = PApplet.join(paragraphs, '\n'); + + // Create a text area with both horizontal and + // vertical scrollbars that automatically hide + // when not needed. + txaSample = new GTextArea(this, 80, 20, 290, 300, G4P.SCROLLBARS_BOTH | G4P.SCROLLBARS_AUTOHIDE); + txaSample.setText(startText, 310); + // Set some default text + txaSample.setDefaultText("Please enter some text"); + + createConfigControls(); +} + +public void draw() { + background(bgcol); + fill(227, 230, 255); + noStroke(); + rect(width - 190, 0, 200, height); +} + +public void handleSliderEvents(GValueControl slider, GEvent event) { + if (slider == sdrBack) + bgcol = slider.getValueI(); +} + +public void handleTextEvents(GEditableTextControl textarea, GEvent event) { + println(event); +} + +public void handleButtonEvents(GButton button, GEvent event) { + if (event == GEvent.CLICKED) { + if (button.tagNo >= 100) + txaSample.setLocalColorScheme(button.tagNo - 1000); + if (button == btnItalic) + txaSample.setSelectedTextStyle(G4P.POSTURE, G4P.POSTURE_OBLIQUE); + if (button == btnPlain) + txaSample.clearStyle(); + if (button == btnBold) + txaSample.setSelectedTextStyle(G4P.WEIGHT, G4P.WEIGHT_BOLD); + if (button == btnSuper) + txaSample.setSelectedTextStyle(G4P.SUPERSCRIPT, G4P.SUPERSCRIPT_SUPER); + if (button == btnSub) + txaSample.setSelectedTextStyle(G4P.SUPERSCRIPT, G4P.SUPERSCRIPT_SUB); + if (button == btnText) + txaSample.setText(startText, 310); + } +} + +public void handleToggleControlEvents(GToggleControl checkbox, GEvent event) { + if (checkbox == cbxJustify) + txaSample.setJustify(checkbox.isSelected()); +} + +public void handleKnobEvents(GValueControl knob, GEvent event) { + txaSample.setRotation(knbAngle.getValueF(), GControlMode.CENTER); +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextAreaControl/configcontrols.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextAreaControl/configcontrols.pde new file mode 100644 index 0000000..0fab6c1 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextAreaControl/configcontrols.pde @@ -0,0 +1,82 @@ +// The code in this tab is used to create all the other +// controls needed to configure the knob control. + +GButton[] btnColours = new GButton[8]; +GButton btnItalic, btnBold, btnPlain, btnSuper, btnSub, btnText; +GCheckbox cbxJustify; +GLabel lblStyleInstr; +GKnob knbAngle; +GSlider sdrBack; +StyledString ss; + +public void createConfigControls() { + int x = width - 42, y = 2; + for (int i = 0; i < btnColours.length; i++) { + btnColours[i] = new GButton(this, x, y + i * 20, 40, 18, "" + (i+1)); + btnColours[i].tag = "Button: " + (i+1); + btnColours[i].setLocalColorScheme(i); + btnColours[i].tagNo = 1000+i; + } + + // Create sliders + x = width-100; + y = 162; + sdrBack = new GSlider(this, x, y, 162, 80, 12); + sdrBack.setLimits(bgcol, 0, 255); + sdrBack.setRotation(-PI/2); + sdrBack.setTextOrientation(G4P.ORIENT_RIGHT); + sdrBack.setEasing(20); + sdrBack.setShowValue(true); + sdrBack.setShowTicks(true); + + x = width -180; + y = 2; + btnPlain = new GButton(this, x, 2, 66, 20, "Clear"); + btnPlain.tag = "Button: Clear"; + + btnItalic = new GButton(this, x, 2 + 22, 66, 20, "Italic"); + btnItalic.tag = "Button: Italic"; + ss = new StyledString("Italic"); + ss.addAttribute(G4P.POSTURE, G4P.POSTURE_OBLIQUE); + btnItalic.setStyledText(ss); + + btnBold = new GButton(this, x, 2 + 44, 66, 20, "Bold"); + btnBold.tag = "Button: Bold"; + ss = new StyledString("Bold"); + ss.addAttribute(G4P.WEIGHT, G4P.WEIGHT_BOLD); + btnBold.setStyledText(ss); + + btnSuper = new GButton(this, x, 2 + 66, 66, 20, "Superscript"); + btnSuper.tag = "Button: Superscript"; + ss = new StyledString("Superscript"); + ss.addAttribute(G4P.SUPERSCRIPT, G4P.SUPERSCRIPT_SUPER, 5, 11); + btnSuper.setStyledText(ss); + + btnSub = new GButton(this, x, 2 + 88, 66, 20, "SubScript"); + btnSub.tag = "Button: Subscript"; + ss = new StyledString("Subscript"); + ss.addAttribute(G4P.SUPERSCRIPT, G4P.SUPERSCRIPT_SUB, 3, 9); + btnSub.setStyledText(ss); + + cbxJustify = new GCheckbox(this, x, 2 + 110, 66, 20, "Justify"); + cbxJustify.setOpaque(true); + + btnText = new GButton(this, x, 142, 66, 36, "RESET TEXT"); + + x = width - 180; + y = 176; + String si = "Use the keyboard (Windows shortcut keys) or the mouse to select some text then click on the style buttons to change the style of the selected text"; + lblStyleInstr = new GLabel(this, x, y, 170, 100, si); + lblStyleInstr.setOpaque(true); + + x = width - 130; + y = height - 80; + knbAngle = new GKnob(this, x, y, 70, 70, 0.6f); + knbAngle.setTurnRange(0, 360); + knbAngle.setLimits(0.0f, 0.0f, TWO_PI); + knbAngle.setTurnMode(G4P.CTRL_ANGULAR); + knbAngle.setIncludeOverBezel(true); + knbAngle.setNbrTicks(13); + knbAngle.setStickToTicks(true); +} + diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextAreaControl/data/book3.txt b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextAreaControl/data/book3.txt new file mode 100644 index 0000000..c6a9d4e --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextAreaControl/data/book3.txt @@ -0,0 +1,3 @@ +Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. +Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. +It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like) \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/G4P_TextIconControls.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/G4P_TextIconControls.pde new file mode 100644 index 0000000..f461436 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/G4P_TextIconControls.pde @@ -0,0 +1,152 @@ +/** + This sketch is used show controls that uae a mixture of + text and icons and allows you to experiment with text + and icon alignment options. + + It also uses the droplist control extensively. + controls available in the G4P library. + + created by Peter Lager + */ + +import g4p_controls.*; + +GToggleGroup tg = new GToggleGroup(); +GOption opt0, opt1, opt2; +GCheckbox cbx0; +GLabel lbl0, lbl1; +GButton btn0, btn1; + +// Since all the classes for all these controls inherit +// from GTextIconControl this arraylist will keep them as +// a collection so they can all be configured as a group +// more easily +ArrayList controls = new ArrayList(); + +int bgcol = 240; + +public void setup() { + size(560, 380); + G4P.setCursorOff(CROSS); + + // Get some sample text for later + String[] lines = loadStrings("tisample.txt"); + String text = join(lines, '\n'); + // Options and checkboxes have default settings : + // left/middle text & left/middle icon alignment + // Labels and buttons have default settings : + // centre/middle text & right/middle icon alignment + // The buttons and labels will have there alignment + // changed to match the labels so they all start + // matched to the drop lists. + lbl0 = new GLabel(this, 20, 10, 150, 350, text); + lbl0.setTextAlign(GAlign.LEFT, null); + controls.add(lbl0); + + lbl1 = new GLabel(this, 200, 100, 140, 80, "Labels can also have icons like this one"); + lbl1.setIcon("bugtest.png", 1, GAlign.LEFT, null); + lbl1.setTextAlign(GAlign.LEFT, null); + controls.add(lbl1); + + btn0 = new GButton(this, 200, 190, 150, 80, "Buttons always have an opaque background"); + btn0.setTextAlign(GAlign.LEFT, null); + controls.add(btn0); + + btn1 = new GButton(this, 200, 280, 150, 80, "As well as text buttons can also have icons"); + btn1.setIcon("smile.png", 3, GAlign.LEFT, null); + btn1.setTextAlign(GAlign.LEFT, null); + controls.add(btn1); + + opt0 = new GOption(this, 200, 10, 100, 18, "Option 0"); + tg.addControl(opt0); + controls.add(opt0); + + opt1 = new GOption(this, 200, 32, 100, 18, "Option 1"); + tg.addControl(opt1); + controls.add(opt1); + + opt2 = new GOption(this, 200, 54, 100, 18, "Option 2"); + tg.addControl(opt2); + controls.add(opt2); + + opt0.setSelected(true); + tg.addControls(opt0, opt1, opt2); + + cbx0 = new GCheckbox(this, 200, 76, 100, 20, "Tick box"); + controls.add(cbx0); + makeTextIconConfigControls(); +} + +public void draw() { + background(bgcol); + fill(227, 230, 255); + noStroke(); + rect(width - 190, 0, 200, height); +} + +public void handleSliderEvents(GValueControl slider, GEvent event) { + if (slider == sdrBack) + bgcol = slider.getValueI(); +} + +public void handleKnobEvents(GValueControl knob, GEvent event) { + if (knbAngle == knob) + for (GTextIconAlignBase control : controls) + control.setRotation(knbAngle.getValueF(), GControlMode.CENTER); +} + +public void handleButtonEvents(GButton button, GEvent event) { + if (button.tagNo >= 1000) { + for (GTextIconAlignBase control : controls) + control.setLocalColorScheme(button.tagNo - 1000); + } +} + + +public void handleToggleControlEvents(GToggleControl option, GEvent event) { + if (option == optPlain) + for (GTextIconAlignBase control : controls) + control.setTextPlain(); + else if (option == optBold) + for (GTextIconAlignBase control : controls) { + control.setTextPlain(); + control.setTextBold(); + } + else if (option == optItalic) + for (GTextIconAlignBase control : controls) { + control.setTextPlain(); + control.setTextItalic(); + } + else if (option == optItalic) + for (GTextIconAlignBase control : controls) { + control.setTextPlain(); + control.setTextItalic(); + } + else if (option == optBoldItalic) + for (GTextIconAlignBase control : controls) { + control.setTextBold(); + control.setTextItalic(); + } + else if (option == cbxOpaque) + for (GTextIconAlignBase control : controls) + control.setOpaque(cbxOpaque.isSelected()); +} + +public void handleDropListEvents(GDropList list, GEvent event) { + GAlign na = GAlign.getFromText(list.getSelectedText()); + println(na); + + if (list == textH) + for (GTextIconAlignBase control : controls) + control.setTextAlign(na, null); + if (list == textV) + for (GTextIconAlignBase control : controls) + control.setTextAlign(null, na); + if (list == iconH) + for (GTextIconAlignBase control : controls) + control.setIconAlign(na, null); + if (list == iconV) { + for (GTextIconAlignBase control : controls) + control.setIconAlign(null, na); + } +} \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/configcontrols.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/configcontrols.pde new file mode 100644 index 0000000..ed388a3 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/configcontrols.pde @@ -0,0 +1,87 @@ + // The code in this tab is used to create all the other + // controls needed to configure the text+icon controls. + + GToggleGroup tgStyle = new GToggleGroup(); + GOption optPlain, optBold, optItalic, optBoldItalic; + GCheckbox cbxOpaque; + GSlider sdrBack; + GLabel lblTestStyle, lblVisible, lblNbrTicks, lblEasing; + GLabel lblTextA, lblIconA; + GKnob knbAngle; + GButton[] btnColours = new GButton[8]; + GDropList textH, textV, iconH, iconV; + + public void makeTextIconConfigControls(){ + // Create colour scheme selectors + int x = width - 42, y = 2; + for(int i = 0; i < btnColours.length; i++){ + btnColours[i] = new GButton(this, x, y + i * 20, 40, 18, "" + (i+1)); + btnColours[i].tag = "Button: " + (i+1); + btnColours[i].setLocalColorScheme(i); + btnColours[i].tagNo = 1000+i; + } + // Create sliders + x = width-100; y = 162; + sdrBack = new GSlider(this, x, y, 162, 80, 12); + sdrBack.setLimits(bgcol, 0, 255); + sdrBack.setRotation(-PI/2); + sdrBack.setTextOrientation(G4P.ORIENT_RIGHT); + sdrBack.setEasing(20); + sdrBack.setShowValue(true); + sdrBack.setShowTicks(true); + + x = width - 180; y = 2; + lblVisible = new GLabel(this, x, y, 80, 18, "Visible"); + lblVisible.setTextBold(); + cbxOpaque = new GCheckbox(this, x, y + 20 , 70, 18, "Opaque"); + + x = width - 180; y = 52; + lblTestStyle = new GLabel(this, x, y, 80, 18, "Text Style"); + lblTestStyle.setTextBold(); + optPlain = new GOption(this, x, y + 20, 80, 18, "Plain"); + optBold = new GOption(this, x, y + 40, 80, 18, "Bold"); + optItalic = new GOption(this, x, y + 60, 80, 18, "Italic"); + optBoldItalic = new GOption(this, x, y + 80, 100, 18, "Bold-Italic"); + + tgStyle.addControls(optPlain, optBold, optItalic, optBoldItalic); + optPlain.setSelected(true); + + // Allignment droplists + String[] items; + x = width - 180; y = 170; + lblTextA = new GLabel(this, x, y, 80, 38, "Text Alignment"); + lblTextA.setTextBold(); + + items = new String[] { "LEFT", "CENTER", "RIGHT", "JUSTIFY"}; + textH = new GDropList(this, x, y + 40, 80, 90, 4); + textH.setItems(items, 0); + textH.tag = "textH"; + + items = new String[] { "TOP", "MIDDLE", "BOTTOM"}; + textV = new GDropList(this, x, y + 60, 80, 90, 4); + textV.setItems(items, 1); + textV.tag = "textV"; + + x = width - 90; y = 170; + lblIconA = new GLabel(this, x, y, 80, 38, "Icon Alignment"); + lblIconA.setTextBold(); + + items = new String[] { "LEFT", "RIGHT"}; + iconH = new GDropList(this, x, y + 40, 80, 90, 4); + iconH.setItems(items, 0); + iconH.tag = "iconH"; + + items = new String[] { "TOP", "MIDDLE", "BOTTOM"}; + iconV = new GDropList(this, x, y + 60, 80, 90, 4); + iconV.setItems(items, 1); + iconV.tag = "iconV"; + + x = width - 130; y = height - 80; + knbAngle = new GKnob(this, x, y, 70, 70, 0.6f); + knbAngle.setTurnRange(0, 360); + knbAngle.setLimits(0.0f, 0.0f, TWO_PI); + knbAngle.setTurnMode(G4P.CTRL_ANGULAR); + knbAngle.setIncludeOverBezel(true); + knbAngle.setNbrTicks(13); + knbAngle.setStickToTicks(true); + } diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/data/bugtest.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/data/bugtest.png new file mode 100644 index 0000000..c619c4c Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/data/bugtest.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/data/smile.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/data/smile.png new file mode 100644 index 0000000..63e0ed1 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/data/smile.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/data/tisample.txt b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/data/tisample.txt new file mode 100644 index 0000000..23c1d83 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_TextIconControls/data/tisample.txt @@ -0,0 +1,3 @@ +Icons are created from images files made by the user. +The user supplied image should contain bewteen 1 and 3 images tiled horizontally. The GButton control the tile show depends on whether the mouse is off or over the control surface and if the mouse button is pressed while over the control. +The GOption and GCheckbox controls need just 2 tiled images and the GLabel just 1 tiled image. \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Timer/G4P_Timer.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Timer/G4P_Timer.pde new file mode 100644 index 0000000..635cbee --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Timer/G4P_Timer.pde @@ -0,0 +1,145 @@ +/** +Balls of Vesuvius. + +A simple program to demonstrate the use of the GTimer +class which is part of the G4P (GUI for Processing) +library. + +(c)2012 Peter Lager + +*/ + +import g4p_controls.*; + +GSlider sdrRate; +GButton btnStart, btnStop; +GTimer timer; + +ArrayList liveBalls, deadBalls; +int rate; +PImage rear, front; + +void setup(){ + size(768,600); + // Create 2 buttons to start and stop the balls + btnStart = new GButton(this, 10, 10, 100, 20, "Start"); + btnStop = new GButton(this, 120, 10, 100, 20, "Stop"); + // Create a slider to control rate of balls erupted. + sdrRate = new GSlider(this, 230, 10, 360, 20, 10); + sdrRate.setLimits(50, 10, 120); // (init, min, max) + sdrRate.setEasing(5); + + // Get timer interval based on initial slider value and limits + rate = 130 - sdrRate.getValueI(); + // Create a GTimer object that will call the method + // fireBall + // Parameter 1 : the PApplet class i.e. 'this' + // 2 : the object that has the method to call + // 3 : the name of the method (parameterless) to call + // 4 : the interval in millisecs bewteen method calls + timer = new GTimer(this, this, "fireBall", rate); + + // Balls in the air alive + liveBalls = new ArrayList(2000); + // Balls that are below the level of the window + deadBalls = new ArrayList(100); + + front = loadImage("vfront.png"); + rear = loadImage("vrear.jpg"); + + // try and keep it at 30fps + frameRate(30); + + // Register the pre() method for this class. Pick the line + // to match the version of Processing being used. + // registerPre(this); // Use this for PS 1.5.1 + registerMethod("pre", this); // Use this for PS 2.0b6 +} + +// This method is now called before each call to draw() +public void pre(){ + Ball b; + int i; + // Update all live balls + for(i = 0; i < liveBalls.size(); i++){ + b = (Ball)liveBalls.get(i); + b.update(); + // See if this ball should die if so remember it + if(b.y > height + 20) + deadBalls.add(b); + } + // Remove dead balls from the list of live balls + for(i = 0; i < deadBalls.size(); i++){ + liveBalls.remove(deadBalls.get(i)); + } + // Done with dead balls + deadBalls.clear(); +} + +void draw(){ + int i; + Ball b; + + background(rear); + for(i = 0; i < liveBalls.size(); i++){ + b = (Ball)liveBalls.get(i); + b.display(); + } + image(front,0,0); +} + +// This is called when the user drags on the slider +void handleSliderEvents(GValueControl slider, GEvent event){ + rate = 130 - sdrRate.getValueI(); + timer.setInterval(rate); +} + +// This method is called when a button is clicked +void handleButtonEvents(GButton button, GEvent event){ + if(button == btnStart && event == GEvent.CLICKED) + timer.start(); + if(button == btnStop && event == GEvent.CLICKED) + timer.stop(); +} + +// This method is called by the timer +void fireBall(GTimer timer){ + Ball ball = new Ball(); + liveBalls.add(ball); +} + +// Simple class to represent a ball +class Ball { + public float radius; + public int col; + public float x, y; + public float vx, vy; + public float gravity = 0.07f; + public float drag = 0.99; + public float shrink = 0.999; + + public Ball(){ + x = random(500,540); + y = 290; + col = color(random(200,255),random(20,55),0); + radius = random(3,10); + vx = random(-3.0, 1.9); + vy = random(5.5, 8.2); + } + + public void update(){ + x += vx; + y -= vy; + vy -= gravity; + if(vy < 0) + vx *= drag; + radius *= shrink; + } + + public void display(){ + noStroke(); + fill(col); + ellipse(x,y,radius,radius); + } + +} \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Timer/data/vfront.png b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Timer/data/vfront.png new file mode 100644 index 0000000..2179669 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Timer/data/vfront.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Timer/data/vrear.jpg b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Timer/data/vrear.jpg new file mode 100644 index 0000000..bb90808 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Timer/data/vrear.jpg differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Toroid/G4P_Toroid.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Toroid/G4P_Toroid.pde new file mode 100644 index 0000000..49c24f3 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Toroid/G4P_Toroid.pde @@ -0,0 +1,128 @@ +/** + Interative Toroid with GUI controls from the G4P + library + (c)2012 Peter Lager + + Modification of the example + Interactive Toroid by Ira Greenberg. + + The toroid code has been abstracted to its own tab to + separate it from the GUI code for clarity. + + Illustrates the geometric relationship between Toroid, Sphere, + and Helix 3D primitives, as well as lathing principal. + + */ + +import g4p_controls.*; + +GLabel lblSegs, lblERad, lblPts, lblLRad; +GCustomSlider sdrSegs, sdrERad, sdrPts, sdrLRad; +GCheckbox cbxWire; +GOption optTorroid, optHelix; +GToggleGroup optShape; +GPanel p; + +Toroid t1; + +void setup() { + size(640, 480, P3D); + t1 = new Toroid(); + + // Sets the colour scheme for the GUI components + // The 8 schemes available are + // RED_SCHEME, GREEN_SCHEME, YELLOW_SCHEME, PURPLE_SCHEME + // ORANGE_SCHEME, CYAN_SCHEME, BLUE_SCHEME, GOLD_SCHEME + // Defaults to BLUE_SCHEME + G4P.setGlobalColorScheme(G4P.GOLD_SCHEME); + + // Create the various GUI components + p = new GPanel(this, 2, height - 30, 460, 300, "Toroid Control Panel"); + lblSegs = new GLabel(this, 2, 40, 120,20, "Segment detail"); + lblPts = new GLabel(this, 2, 100, 120, 20 , "Ellipse detail"); + lblERad = new GLabel(this, 2, 160, 120, 20, "Ellipse Radius"); + lblLRad = new GLabel(this, 2, 220, 120, 20, "Toroid Radius"); + + sdrSegs = new GCustomSlider(this, 110, 20, 325, 60, "purple18px"); + sdrSegs.setLimits(60, 3, 60); + sdrSegs.setNbrTicks(58); + sdrSegs.setStickToTicks(true); + + sdrPts = new GCustomSlider(this, 110, 80, 325, 60, "purple18px"); + sdrPts.setLimits(32, 3, 32); + sdrPts.setNbrTicks(30); + sdrPts.setStickToTicks(true); + + sdrERad = new GCustomSlider(this, 110, 140, 325, 60, null); + sdrERad.setLimits(60.0, 10.0, 100.0); + sdrERad.setEasing(20); + + sdrLRad = new GCustomSlider(this, 110, 200, 325, 60, null); + sdrLRad.setLimits(140.0, 10.0, 240.0); + sdrLRad.setEasing(20); + + // Various options + optTorroid = new GOption(this, 110, 260, 80, 20, "Toroid?"); + optHelix = new GOption(this, 200, 260, 80, 20 , "Helix?"); + cbxWire = new GCheckbox(this, 330, 260, 100, 20, "Wire frame?"); + + // Torroid / helix option group + optShape = new GToggleGroup(); + optShape.addControl(optTorroid); + optShape.addControl(optHelix); + optTorroid.setSelected(true); + + p.addControl(lblSegs); + p.addControl(lblPts); + p.addControl(lblERad); + p.addControl(lblLRad); + p.addControl(sdrSegs); + p.addControl(sdrPts); + p.addControl(sdrERad); + p.addControl(sdrLRad); + p.addControl(optHelix); + p.addControl(optTorroid); + p.addControl(cbxWire); + // Set the alpha after adding the controls. + // The true will mean it will be applied to + // anything already added to the panel. + p.setAlpha(200, true); +} + +public void handleSliderEvents(GValueControl slider, GEvent event) { + if (slider == sdrSegs) + t1.setSegmentDetail(sdrSegs.getValueI()); + if (slider == sdrPts) + t1.setEllipseDetail(sdrPts.getValueI()); + if (slider == sdrERad) + t1.setEllipseRadius(sdrERad.getValueF()); + if (slider == sdrLRad) + t1.setLatheRadius(sdrLRad.getValueF()); +} + +public void handleToggleControlEvents(GToggleControl option, GEvent event) { + if (option == cbxWire) + t1.setIsWire(cbxWire.isSelected()); + if (option == optHelix) + t1.setIsHelix(true); + if (option == optTorroid) + t1.setIsHelix(false); +} + +void draw() { + pushMatrix(); + background(200, 200, 255); + // basic lighting setup + lights(); + // 2 rendering styles + //center and spin toroid + translate(width/2, height/2, -200); + + rotateX(frameCount*PI/150); + rotateY(frameCount*PI/170); + rotateZ(frameCount*PI/90); + + // draw toroid + t1.draw(); + popMatrix(); +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Toroid/Toroid.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Toroid/Toroid.pde new file mode 100644 index 0000000..21ea5d1 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_Toroid/Toroid.pde @@ -0,0 +1,130 @@ +/** + * Interactive Toroid + * by Ira Greenberg. + * + * Illustrates the geometric relationship between Toroid, Sphere, + * and Helix 3D primitives, as well as lathing principal. + */ + +class Toroid { + int pts = 40; + float angle = 0; + float radius = 60.0; + + // lathe segments + int segments = 60; + float latheAngle = 0; + float latheRadius = 100.0; + + //vertices + PVector vertices[], vertices2[]; + + // for shaded or wireframe rendering + boolean isWireFrame = false; + + // for optional helix + boolean isHelix = false; + float helixOffset = 5.0; + + + boolean modelChange = false; + + public Toroid(){ + fillVertexArrays(); + } + + public void setSegmentDetail(int segments){ + this.segments = segments; + fillVertexArrays(); + } + + public void setEllipseDetail(int points){ + pts = points; + fillVertexArrays(); + } + + public void setEllipseRadius(float eradius){ + radius = eradius; + fillVertexArrays(); + } + + public void setLatheRadius(float lradius){ + latheRadius = lradius; + fillVertexArrays(); + } + + public void setIsHelix(boolean helix){ + isHelix = helix; + fillVertexArrays(); +} + + public void setIsWire(boolean wire){ + isWireFrame = wire; + } + + void fillVertexArrays(){ + // initialize point arrays + vertices = new PVector[pts+1]; + vertices2 = new PVector[pts+1]; + + // fill arrays + for(int i=0; i<=pts; i++){ + vertices[i] = new PVector(); + vertices2[i] = new PVector(); + vertices[i].x = latheRadius + sin(radians(angle))*radius; + if (isHelix){ + vertices[i].z = cos(radians(angle))*radius-(helixOffset* segments)/2; + } + else{ + vertices[i].z = cos(radians(angle))*radius; + } + angle+=360.0/pts; + } + } + + void draw(){ + pushStyle(); + if(isHelix) + fillVertexArrays(); + pushMatrix(); + if (isWireFrame){ + stroke(64, 64, 128); + strokeWeight(1); + noFill(); + } + else { + noStroke(); + fill(40, 40, 255); + } + + // draw toroid + latheAngle = 0; + for(int i=0; i<=segments; i++){ + beginShape(QUAD_STRIP); + for(int j=0; j<=pts; j++){ + if (i>0){ + vertex(vertices2[j].x, vertices2[j].y, vertices2[j].z); + } + vertices2[j].x = cos(radians(latheAngle))*vertices[j].x; + vertices2[j].y = sin(radians(latheAngle))*vertices[j].x; + vertices2[j].z = vertices[j].z; + // optional helix offset + if (isHelix){ + vertices[j].z+=helixOffset; + } + vertex(vertices2[j].x, vertices2[j].y, vertices2[j].z); + } + // create extra rotation for helix + if (isHelix){ + latheAngle+=720.0/segments; + } + else { + latheAngle+=360.0/segments; + } + endShape(); + } + popMatrix(); + popStyle(); + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_WindowsStarter/G4P_WindowsStarter.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_WindowsStarter/G4P_WindowsStarter.pde new file mode 100644 index 0000000..0011130 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_WindowsStarter/G4P_WindowsStarter.pde @@ -0,0 +1,121 @@ +/* + Demonstration of how to create and use multiple windows with + the G4P (GUI for Processing) library. + + (c)2012 Peter Lager + + Since each window has its own unique data set they can all + share the code needed for mouse handling and drawing. + It is a complex example and it is recommended that you read + a detailed explanation of the code used which can be found at: + http://www.lagers.org.uk/g4p/applets/g4p_windowsstarter + + */ + +import g4p_controls.*; + +GWindow[] window; +GButton btnStart; +GLabel lblInstr; + +void setup() { + size(256, 128); + btnStart = new GButton(this, 4, 34, 120, 60, "Create 3 Windows"); + lblInstr = new GLabel(this, 132, 34, 120, 60, "Use the mouse to draw a rectangle in any of the 3 windows"); + lblInstr.setTextAlign(GAlign.CENTER, GAlign.MIDDLE); + lblInstr.setVisible(false); +} + +/** + * Draw for the main window + */ +void draw() { + background(240); +} + +/** +Create the three windows so that they share mouse handling +and drawing code. +*/ +void createWindows() { + int col; + window = new GWindow[3]; + for (int i = 0; i < 3; i++) { + col = (128 << (i * 8)) | 0xff000000; + window[i] = new GWindow(this, "Window "+i, 70+i*220, 160+i*50, 200, 200, false, JAVA2D); + window[i].setBackground(col); + window[i].addData(new MyWinData()); + window[i].addDrawHandler(this, "windowDraw"); + window[i].addMouseHandler(this, "windowMouse"); + } +} + +/** + * Click the button to create the windows. + * @param button + */ +void handleButtonEvents(GButton button, GEvent event) { + if (window == null && event == GEvent.CLICKED) { + createWindows(); + lblInstr.setVisible(true); + button.setEnabled(false); + } +} + +/** + * Handles mouse events for ALL GWindow objects + * + * @param appc the PApplet object embeded into the frame + * @param data the data for the GWindow being used + * @param event the mouse event + */ +void windowMouse(GWinApplet appc, GWinData data, MouseEvent event) { + MyWinData data2 = (MyWinData)data; + switch(event.getAction()) { + case MouseEvent.PRESS: + data2.sx = data2.ex = appc.mouseX; + data2.sy = data2.ey = appc.mouseY; + data2.done = false; + break; + case MouseEvent.RELEASE: + data2.ex = appc.mouseX; + data2.ey = appc.mouseY; + data2.done = true; + break; + case MouseEvent.DRAG: + data2.ex = appc.mouseX; + data2.ey = appc.mouseY; + break; + } +} + +/** + * Handles drawing to the windows PApplet area + * + * @param appc the PApplet object embeded into the frame + * @param data the data for the GWindow being used + */ +void windowDraw(GWinApplet appc, GWinData data) { + MyWinData data2 = (MyWinData)data; + if (!(data2.sx == data2.ex && data2.ey == data2.ey)) { + appc.stroke(255); + appc.strokeWeight(2); + appc.noFill(); + if (data2.done) { + appc.fill(128); + } + appc.rectMode(CORNERS); + appc.rect(data2.sx, data2.sy, data2.ex, data2.ey); + } +} + +/** + * Simple class that extends GWinData and holds the data + * that is specific to a particular window. + * + * @author Peter Lager + */ +class MyWinData extends GWinData { + int sx, sy, ex, ey; + boolean done; +} \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_with_PeasyCam/G4P_with_PeasyCam.pde b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_with_PeasyCam/G4P_with_PeasyCam.pde new file mode 100644 index 0000000..02453d4 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/examples/G4P_with_PeasyCam/G4P_with_PeasyCam.pde @@ -0,0 +1,201 @@ +/** + * PeasyCam with GUI control + * + * Extends PeasyCam example to use with G4P by providing + * sliders to control the angles. + * + * When the panel is open you can drag the sliders to rotate + * the cube. When the panel is closed then dragging the mouse + * over the display will rotate the cube as per PeaseyCam + * example. + * + * The control code is quite complex because PeasyCam does + * not provide methods to set the rotation angles abso;utely + * only relatively. + * + * Click on the panel tab to open/close the panel. + * + * by Peter Lager + */ + +import g4p_controls.*; +import peasy.*; + +PeasyCam cam; + +GPanel pnl; +GSlider sx, sy, sz; +GLabel label; +int ax, ay, az; + +// These are needed to remember PeasyCam offset and distance +float[] offsets = new float[3]; +float[] rotations = new float[3]; +double distance = 0.0f; + +// Remember last slider values +// PeasyCam does not provide methods to set the absolute +// rotation angles, rotateX(ang) rotates about the X axis +// by ang radians +int lastSx, lastSy, lastSz; +int currSx, currSy, currSz; + +public void setup() { + size(400, 400, P3D); + + // This is only required if you are going to create something + // that changes the initial matrix (g) BEFORE creating a G4P + // control. PeasyCam is just such an object, but if it is + // created after one of the G4P controls then you would not + // this statement. + G4P.registerSketch(this); + + // Create a PeasyCam object + cam = new PeasyCam(this, 100); + cam.setMinimumDistance(50); + cam.setMaximumDistance(500); + + // Sets the colour scheme for the GUI components + // The 8 schemes available are + // RED_SCHEME, GREEN_SCHEME, YELLOW_SCHEME, PURPLE_SCHEME + // ORANGE_SCHEME, CYAN_SCHEME, BLUE_SCHEME, GOLD_SCHEME + // Defaults to BLUE_SCHEME + G4P.setGlobalColorScheme(GCScheme.RED_SCHEME); + + // Create a collapsible panel + // (this, tab title, x, y, width, height) + pnl = new GPanel(this, 10, 300, 300, 88, "Rotate Cube"); + pnl.setCollapsed(false); + // Create a horizontal slider + // (this, x, y, width, height) + // default value limits 0-100 and initial value 50 + sx = new GSlider(this, 10, 22, 280, 20, 13); + // set slider value limits (initial value, min, max) + sx.setLimits(0, -180, 180); + // Hide tick marks + sx.setShowTicks(false); + // Set thumb easing for nice visual effect - acceptable + // values 1 to 100 (default = 1 i.e. no easing) + sx.setEasing(15); + + sy = new GSlider(this, 10, 42, 280, 20, 13); + sy.setLimits(0, -180, 180); + sy.setShowTicks(false); + sy.setEasing(15); + + sz = new GSlider(this, 10, 62, 280, 20, 13); + sz.setLimits(0, -180, 180); + sz.setShowTicks(false); + sz.setEasing(15); + + // Add the sliders to the panel x,y coordinates are now + // relative to the top left of the panel open area below + // the tab + pnl.addControl(sx); + pnl.addControl(sy); + pnl.addControl(sz); + + // Create a label across the top of the screen + String s = "Drag the slider thumb or click on the "; + s += "track to rotate the cube. The panel can be collapsed/"; + s += "expanded by clicking on the panel title bar"; + label = new GLabel(this, 0, 0, width, 60, s); + label.setOpaque(true); + // Align the text both horizontally and vertically + label.setTextAlign(GAlign.CENTER, GAlign.MIDDLE); +} + +public void draw() { + // Switch off PeasyCam mouse control if the panel is being + // dragged else turn it on + if (pnl.isCollapsed()) // Panel is collapsed + cam.setMouseControlled(!pnl.isDragging()); + else // panel open must be using sliders + cam.setMouseControlled(false); + rotateX(-.5f); + rotateY(-.5f); + background(0); + // Draw big box + strokeWeight(2); + stroke(255, 255, 0); + fill(255, 0, 0); + box(30); + // Draw little box + translate(0, 0, 20); + fill(0, 0, 255); + box(5); + // Synchronise the actual rotations and slider positions + syncSliders(); +} + + +/* + This function displays how we can create a HUD with PeasyCam. + */ +void syncSliders() { + // Get the current PeasyCam details to restore later + rotations = cam.getRotations(); + + // If necessary update slider positions + if (pnl.isCollapsed()) { + // Update slider positions + currSx = lastSx = (int)Math.toDegrees(rotations[0]); + currSy = lastSy = (int)Math.toDegrees(rotations[1]); + currSz = lastSz = (int)Math.toDegrees(rotations[2]); + + // There are 2 methods to set the value of the slider + // setValue(value); it takes into account any inertia + // setValue(value, ignore); where ignore is a boolean value + // which if true will set the value and move the thumb + // immediately ignoring any inertia value + sx.setValue((int)Math.toDegrees(rotations[0])); + sy.setValue((int)Math.toDegrees(rotations[1])); + sz.setValue((int)Math.toDegrees(rotations[2])); + } + else { // Use sliders to control rotation + if (currSx != lastSx) { + cam.rotateX(Math.toRadians(currSx - lastSx)); + lastSx = currSx; + } + if (currSy != lastSy) { + cam.rotateY(Math.toRadians(currSy - lastSy)); + lastSy = currSy; + } + if (currSz != lastSz) { + cam.rotateZ(Math.toRadians(currSz - lastSz)); + lastSz = currSz; + } + } +} + +// Handle panels events i.e. when a panel is opened or +// collapsed +void handlePanelEvents(GPanel panel, GEvent event) { + // Intended to detect panel events but ended up not + // needing it. Left the following code as an example + switch(event) { + case COLLAPSED: + pnl.setAvailableChildren(false); + println("Panel has collapsed"); + break; + case EXPANDED: + pnl.setAvailableChildren(true); + println("Panel has expanded"); + break; + case DRAGGED: + print("The panel has been dragged to "); + println(pnl.getX() + ", " + pnl.getY()); + break; + } +} + +// Handles slider events for both horizontal and +// vertical sliders +void handleSliderEvents(GValueControl slider, GEvent event) { + if (slider == sx) + currSx = slider.getValueI(); + if (slider == sy) + currSy = slider.getValueI(); + if (slider == sz) + currSz = slider.getValueI(); +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/library.properties b/host software/processing libraries/sketchbook/libraries/G4P/library.properties new file mode 100644 index 0000000..9535fae --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/library.properties @@ -0,0 +1,49 @@ +# More on this file here: http://code.google.com/p/processing/wiki/LibraryBasics +# UTF-8 supported. + +# The name of your library as you want it formatted. +name = G4P + +# List of authors. Links can be provided using the syntax [author name](url). +authorList = [Peter Lager](http://www.lagers.org.uk) + +# A web page for your library, NOT a direct link to where to download it. +url = http://www.lagers.org.uk/g4p + +# The category of your library, must be one (or many) of the following: +# "3D" "Animation" "Compilations" "Data" +# "Fabrication" "Geometry" "GUI" "Hardware" +# "I/O" "Language" "Math" "Simulation" +# "Sound" "Utilities" "Typography" "Video & Vision" +# +# If a value other than those listed is used, your library will listed as +# "Other". +category = GUI + +# A short sentence (or fragment) to summarize the library's function. This will +# be shown from inside the PDE when the library is being installed. Avoid +# repeating the name of your library here. Also, avoid saying anything redundant +# like mentioning that it's a library. This should start with a capitalized +# letter, and end with a period. +sentence = Provides a set of 2D GUI controls and multiple window support. + +# Additional information suitable for the Processing website. The value of +# 'sentence' always will be prepended, so you should start by writing the +# second sentence here. If your library only works on certain operating systems, +# mention it here. +paragraph = Controls include buttons, sliders, knobs, labels, textfields, timers, combo boxes, option buttons etc. and multiple windows. [G4P GUI builder](http://www.lagers.org.uk/g4ptool/index.html) is a companion tool that enables the rapid (visual) development of user interfaces with this library. + +# Links in the 'sentence' and 'paragraph' attributes can be inserted using the +# same syntax as for authors. +# That is, [here is a link to Processing](http://processing.org/) + + +# A version number that increments once with each release. This is used to +# compare different versions of the same library, and check if an update is +# available. You should think of it as a counter, counting the total number of +# releases you've had. +version = 15 # This must be parsable as an int + +# The version as the user will see it. If blank, the version attribute will be +# used here. +prettyVersion = 3.3 # This is treated as a String diff --git a/host software/processing libraries/sketchbook/libraries/G4P/library/G4P.jar b/host software/processing libraries/sketchbook/libraries/G4P/library/G4P.jar new file mode 100644 index 0000000..8b3bc76 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/library/G4P.jar differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/library/core.jar b/host software/processing libraries/sketchbook/libraries/G4P/library/core.jar new file mode 100644 index 0000000..4adc0d0 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/library/core.jar differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/centre.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/centre.png new file mode 100644 index 0000000..6b11682 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/centre.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/end_left.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/end_left.png new file mode 100644 index 0000000..d794461 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/end_left.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/end_right.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/end_right.png new file mode 100644 index 0000000..569f43a Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/end_right.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/handle.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/handle.png new file mode 100644 index 0000000..6d9ced6 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/handle.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/handle_mouseover.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/handle_mouseover.png new file mode 100644 index 0000000..0c3be66 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/blue18px/handle_mouseover.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/combo0.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/combo0.png new file mode 100644 index 0000000..b9cbf23 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/combo0.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/default_gui_palette.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/default_gui_palette.png new file mode 100644 index 0000000..01204e5 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/default_gui_palette.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/err0.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/err0.png new file mode 100644 index 0000000..91ef9ae Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/err0.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/err1.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/err1.png new file mode 100644 index 0000000..3f17af5 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/err1.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/err2.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/err2.png new file mode 100644 index 0000000..dc3cfcc Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/err2.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/centre.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/centre.png new file mode 100644 index 0000000..eab6bd0 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/centre.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/end_left.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/end_left.png new file mode 100644 index 0000000..3e730dc Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/end_left.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/end_right.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/end_right.png new file mode 100644 index 0000000..1b4ff65 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/end_right.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/handle.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/handle.png new file mode 100644 index 0000000..3a00333 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/handle.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/handle_mouseover.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/handle_mouseover.png new file mode 100644 index 0000000..40b5a3e Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/green_red20px/handle_mouseover.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/centre.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/centre.png new file mode 100644 index 0000000..8c0c09f Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/centre.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/end_left.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/end_left.png new file mode 100644 index 0000000..5424edf Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/end_left.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/end_right.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/end_right.png new file mode 100644 index 0000000..273d188 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/end_right.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/full_back.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/full_back.png new file mode 100644 index 0000000..34b8684 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/full_back.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/handle.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/handle.png new file mode 100644 index 0000000..02c219d Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/handle.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/handle_mouseover.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/handle_mouseover.png new file mode 100644 index 0000000..53e423a Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/grey_blue/handle_mouseover.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/noimage3.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/noimage3.png new file mode 100644 index 0000000..beef91e Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/noimage3.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/pinhead.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/pinhead.png new file mode 100644 index 0000000..4d50bd5 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/pinhead.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/centre.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/centre.png new file mode 100644 index 0000000..6b11682 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/centre.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/end_left.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/end_left.png new file mode 100644 index 0000000..d794461 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/end_left.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/end_right.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/end_right.png new file mode 100644 index 0000000..569f43a Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/end_right.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/handle.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/handle.png new file mode 100644 index 0000000..4cfece5 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/handle.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/handle_mouseover.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/handle_mouseover.png new file mode 100644 index 0000000..2ef2286 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/purple18px/handle_mouseover.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/centre.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/centre.png new file mode 100644 index 0000000..6b11682 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/centre.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/end_left.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/end_left.png new file mode 100644 index 0000000..d794461 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/end_left.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/end_right.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/end_right.png new file mode 100644 index 0000000..569f43a Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/end_right.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/handle.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/handle.png new file mode 100644 index 0000000..ecb3784 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/handle.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/handle_mouseover.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/handle_mouseover.png new file mode 100644 index 0000000..fc153a7 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/red_yellow18px/handle_mouseover.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/tick.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/tick.png new file mode 100644 index 0000000..22118e8 Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/tick.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/data/toggle.png b/host software/processing libraries/sketchbook/libraries/G4P/src/data/toggle.png new file mode 100644 index 0000000..c71a49e Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/G4P/src/data/toggle.png differ diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/ColorPreviewPanel.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/ColorPreviewPanel.java new file mode 100644 index 0000000..d736ada --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/ColorPreviewPanel.java @@ -0,0 +1,72 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2013 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.awt.Color; +import java.awt.FlowLayout; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.colorchooser.ColorSelectionModel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * The preview panel class for the color selector. + * + * @author Peter Lager + * + */ +@SuppressWarnings("serial") +class ColorPreviewPanel extends JPanel implements ChangeListener { + JLabel lblPrev, lblCurr; + JLabel lblPrevColor, lblCurrColor; + + + public ColorPreviewPanel(Color c){ + setLayout(new FlowLayout()); + lblPrev = new JLabel("Initial Color"); + lblCurr = new JLabel("Current Color"); + lblPrevColor = new JLabel(" "); + lblPrevColor.setOpaque(true); + lblPrevColor.setBackground(c); + lblCurrColor = new JLabel(" "); + lblCurrColor.setOpaque(true); + lblCurrColor.setBackground(c); + add(lblCurr); + add(lblCurrColor); + add(lblPrevColor); + add(lblPrev); + } + + + public void stateChanged(ChangeEvent e) { + ColorSelectionModel csm = (ColorSelectionModel)e.getSource(); + lblCurrColor.setBackground(csm.getSelectedColor()); + } + + public void setPrevColor(Color pcol){ + lblPrevColor.setBackground(pcol); + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/FileChooserFilter.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/FileChooserFilter.java new file mode 100644 index 0000000..92b173a --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/FileChooserFilter.java @@ -0,0 +1,71 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2012 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.io.File; +import javax.swing.filechooser.FileFilter; +import processing.core.PApplet; + +class FileChooserFilter extends FileFilter { + + private final String[] ftypes; + private String description = null; + + public FileChooserFilter(String types){ + this(types, null); + } + + public FileChooserFilter(String types, String desc){ + ftypes = PApplet.split(types.toLowerCase(), ','); + for(String e : ftypes) + e = e.trim(); + description = desc; + } + + @Override + public boolean accept(File f) { + String fext = getExtension(f); + if(fext != null){ + for(String e : ftypes) + if(fext.equals(e)) + return true; + } + return false; + } + + @Override + public String getDescription() { + return description; + } + + public String getExtension(File f) { + String ext = null; + String s = f.getName(); + int i = s.lastIndexOf('.'); + + if (i > 0 && i < s.length() - 1) + ext = s.substring(i+1).toLowerCase(); + return ext; + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/FilenameChooserFilter.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/FilenameChooserFilter.java new file mode 100644 index 0000000..c7005e0 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/FilenameChooserFilter.java @@ -0,0 +1,54 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2012 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.io.File; +import java.io.FilenameFilter; + +import processing.core.PApplet; + +class FilenameChooserFilter implements FilenameFilter { + + private final String[] ftypes; + + public FilenameChooserFilter(String types){ + ftypes = PApplet.split(types.toLowerCase(), ','); + for(String e : ftypes) + e = e.trim(); + } + + public boolean accept(File dir, String name) { + String fext = null; + int i = name.lastIndexOf('.'); + if (i > 0 && i < name.length() - 1) + fext = name.substring(i+1).toLowerCase(); + if(fext != null){ + for(String e : ftypes) + if(fext.equals(e)) + return true; + } + return false; + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/G4P.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/G4P.java new file mode 100644 index 0000000..cdb0c59 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/G4P.java @@ -0,0 +1,820 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-13 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + + +import java.awt.Color; +import java.awt.FileDialog; +import java.awt.Font; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.colorchooser.AbstractColorChooserPanel; +import javax.swing.filechooser.FileFilter; + +import processing.core.PApplet; +import processing.core.PConstants; + +/** + * The core class for the global manipulation and execution of G4P.
+ * It also gives access to many of the constants used in this library. + * + * @author Peter Lager + * + */ +public class G4P implements GConstants, PConstants { + + static PApplet sketchApplet = null; + + public static GWindowCloser windowCloser = null; + + /** + * return the pretty version of the library. + */ + public static String getPrettyVersion() { + return "3.3"; + } + + /** + * return the version of the library used by Processing + */ + public static String getVersion() { + return "15"; + } + + static int globalColorScheme = GCScheme.BLUE_SCHEME; + static int globalAlpha = 255; + + /** + * Java has cross platform support for 5 logical fonts so use one of these + * in preference to platform specific fonts or include them here. + *
    + *
  • Dialog
  • + *
  • DialogInput
  • t + *
  • Monospaced
  • + *
  • Serif
  • + *
  • SansSerif
  • + *
+ */ + static Font globalFont = new Font("Dialog", Font.PLAIN, 12); + static Font numericLabelFont = new Font("DialogInput", Font.BOLD, 12); + + // Store of info about windows and controls + static HashMap windows = new HashMap(); + // Used to order controls + static GAbstractControl.Z_Order zorder = new GAbstractControl.Z_Order(); + + /* INTERNAL USE ONLY Mouse over changer */ + static boolean cursorChangeEnabled = true; + static int mouseOff = ARROW; + + static boolean showMessages = true; + + // Determines how position and size parameters are interpreted when + // a control is created + // Introduced V3.0 + static GControlMode control_mode = GControlMode.CORNER; + + static LinkedList styles = new LinkedList(); + + static JColorChooser chooser = null; + static Color lastColor = Color.white; // White + + /** + * Used to register the main sketch window with G4P. This is ignored if any + * G4P controls or windows have already been created because the act of + * creating a control will do this for you.
+ * + * Some controls are created without passing a reference to the sketch applet + * but still need to know it. An example is the GColorChooser control which + * cannot be used until this method is called or some other G4P control has + * been created. + * + * Also some other libraries such as PeasyCam change the transformation matrix. + * In which case either a G4P control should be created or this method called + * before creating a PeasyCam object. + * + * @param app + */ + public static void registerSketch(PApplet app){ + if(sketchApplet == null) { + sketchApplet = app; + GWindowInfo winfo = windows.get(app); + if(winfo == null){ + winfo = new GWindowInfo(app); + windows.put(app, winfo); + } + } + } + + /** + * Set the global colour scheme. This will change the local + * colour scheme for every control. + * @param cs colour scheme to use (0-15) + */ + public static void setGlobalColorScheme(int cs){ + cs = Math.abs(cs) % 16; // Force into valid range + if(globalColorScheme != cs){ + globalColorScheme = cs; + for(GWindowInfo winfo : windows.values()) + winfo.setColorScheme(globalColorScheme); + } + } + + /** + * Set the colour scheme for all the controls drawn by the given + * PApplet. This will override any previous colour scheme for + * these controls. + * @param app + * @param cs + */ + public static void setWindowColorScheme(PApplet app, int cs){ + cs = Math.abs(cs) % 16; // Force into valid range + GWindowInfo winfo = windows.get(app); + if(winfo != null) + winfo.setColorScheme(cs); + } + + /** + * Set the colour scheme for all the controls drawn by the given + * GWindow. This will override any previous colour scheme for + * these controls. + * @param win + * @param cs + */ + public static void setWindowColorScheme(GWindow win, int cs){ + cs = Math.abs(cs) % 16; // Force into valid range + GWindowInfo winfo = windows.get(win.papplet); + if(winfo != null) + winfo.setColorScheme(cs); + } + + + /** + * Set the transparency of all controls. If the alpha level for a + * control falls below G4P.ALPHA_BLOCK then it will no longer + * respond to mouse and keyboard events. + * + * @param alpha value in the range 0 (transparent) to 255 (opaque) + */ + public static void setGlobalAlpha(int alpha){ + alpha = Math.abs(alpha) % 256; // Force into valid range + if(globalAlpha != alpha){ + globalAlpha = alpha; + for(GWindowInfo winfo : windows.values()) + winfo.setAlpha(globalAlpha); + } + } + + /** + * Set the transparency level for all controls drawn by the given + * PApplet. If the alpha level for a control falls below + * G4P.ALPHA_BLOCK then it will no longer respond to mouse + * and keyboard events. + * + * @param app + * @param alpha value in the range 0 (transparent) to 255 (opaque) + */ + public static void setWindowAlpha(PApplet app, int alpha){ + alpha = Math.abs(alpha) % 256; // Force into valid range + GWindowInfo winfo = windows.get(app); + if(winfo != null) + winfo.setAlpha(alpha); + } + + /** + * Set the transparency level for all controls drawn by the given + * GWindow. If the alpha level for a control falls below + * G4P.ALPHA_BLOCK then it will no longer respond to mouse + * and keyboard events. + * + * @param win apply to this window + * @param alpha value in the range 0 (transparent) to 255 (opaque) + */ + public static void setWindowAlpha(GWindow win, int alpha){ + alpha = Math.abs(alpha) % 256; // Force into valid range + GWindowInfo winfo = windows.get(win.papplet); + if(winfo != null) + winfo.setAlpha(alpha); + } + + /** + * Register a GWindow object. + * + * @param window + */ + static void addWindow(GWindow window){ + PApplet app = window.papplet; + GWindowInfo winfo = windows.get(app); + if(winfo == null){ + winfo = new GWindowInfo(app); + windows.put(app, winfo); + } + // Create and start windows closer object + if(windowCloser == null){ + windowCloser = new GWindowCloser(); + sketchApplet.registerMethod("post", windowCloser); + } + } + + /** + * This is called by the GWindow's WindowAdapter when it detects a + * WindowClosing event. It adds this window to a list of windows to + * be closed by the GWindowCloser object in its 'post' method. + * + * @param window the GWindow to be closed + */ + static void markWindowForClosure(GWindow window){ + windowCloser.addWindow(window); + } + + /** + * Used internally to register a control with its applet. + * @param control + */ + static void addControl(GAbstractControl control){ + PApplet app = control.getPApplet(); + // The first applet must be the sketchApplet + if(G4P.sketchApplet == null) + G4P.sketchApplet = app; + GWindowInfo winfo = windows.get(app); + if(winfo == null){ + winfo = new GWindowInfo(app); + windows.put(app, winfo); + } + winfo.addControl(control); + } + + /** + * Remove a control from the window. This is used in preparation + * for disposing of a control. + * @param control + * @return true if control was remove else false + */ + static boolean removeControl(GAbstractControl control){ + PApplet app = control.getPApplet(); + GWindowInfo winfo = windows.get(app); + if(winfo != null){ + winfo.removeControl(control); + return true; + } + return false; + } + + /** + * Change the way position and size parameters are interpreted when a control is created. + * or added to another control e.g. GPanel.
+ * There are 3 modes.
+	 * PApplet.CORNER	 (x, y, w, h) 
+ * PApplet.CORNERS (x0, y0, x1, y1)
+ * PApplet.CENTER (cx, cy, w, h)

+ * + * @param mode illegal values are ignored leaving the mode unchanged + */ + public static void setCtrlMode(GControlMode mode){ + if(mode != null) + control_mode = mode; + } + + /** + * Get the control creation mode @see ctrlMode(int mode) + * @return the current control mode + */ + public static GControlMode getCtrlMode(){ + return control_mode; + } + + /** + * G4P has a range of support messages eg
if you create a GUI component + * without an event handler or,
a slider where the visible size of the + * slider is less than the difference between min and max values. + * + * This method allows the user to enable (default) or disable this option. If + * disable then it should be called before any GUI components are created. + * + * @param enable + */ + public static void messagesEnabled(boolean enable){ + showMessages = enable; + } + + /** + * Enables or disables cursor over component change.
+ * + * Calls to this method are ignored if no G4P controls have been created. + * + * @param enable true to enable cursor change over components. + */ + public static void setMouseOverEnabled(boolean enable){ + cursorChangeEnabled = enable; + } + + /** + * Inform G4P which cursor shapes will be used. + * Initial values are ARROW (off) and HAND (over) + * use setCursor method + * @param cursorOff + * + * @deprecated use setCursor(int) + */ + @Deprecated + public static void setCursorOff(int cursorOff){ + mouseOff = cursorOff; + } + + public static void setCursor(int cursorOff){ + mouseOff = cursorOff; + for(GWindowInfo winfo : windows.values()) + winfo.app.cursor(cursorOff); + } + + public static void setCursor(int cursorOff, GWindow window){ + PApplet app = window.papplet; + setCursor(cursorOff, app); + } + + public static void setCursor(int cursorOff, PApplet app){ + GWindowInfo winfo = windows.get(app); + if(winfo != null){ + mouseOff = cursorOff; + winfo.app.cursor(cursorOff); + } + } + + /** + * Inform G4P which cursor to use for mouse over. + * + */ + public static int getCursor(){ + return mouseOff; + } + + /** + * @deprecated use getCursor() + */ + @Deprecated + public static int getCursorOff(){ + return mouseOff; + } + + /** + * Save the current style on a stack.
+ * There should be a matching popStyle otherwise the program it will + * cause a memory leakage. + */ + static void pushStyle(){ + G4Pstyle s = new G4Pstyle(); + s.ctrlMode = control_mode; + s.showMessages = showMessages; + // Now save the style for later + styles.addLast(s); + } + + /** + * Remove and restore the current style from the stack.
+ * There should be a matching pushStyle otherwise the program will crash. + */ + static void popStyle(){ + G4Pstyle s = styles.removeLast(); + control_mode = s.ctrlMode; + showMessages = s.showMessages; + } + + /** + * This class represents the current style used by G4P. + * It can be extended to add other attributes but these should be + * included in the pushStyle and popStyle. + * @author Peter + * + */ + static class G4Pstyle { + GControlMode ctrlMode; + boolean showMessages; + } + + /** + * Get a list of all open GWindow objects even if minimised or invisible.
+ * If an ArrayList is provided then its contents are cleared before adding references + * to all open GWindow objects. If an ArrayList is not provided then a new + * ArrayList will be created.
+ * This method never returns null, if there are no open windows the list will + * be of size zero. + * + * @param list an optional ArrayList to use. In null will create a new ArrayList. + * @return an ArrayList of references to all open GWindow objects. + */ + public static ArrayList getOpenWindowsAsList(ArrayList list){ + if(list == null) + list = new ArrayList(); + else + list.clear(); + Collection windowInfos = windows.values(); + for(GWindowInfo info : windowInfos){ + if(info.isGWindow) + list.add( ((GWinApplet)info.app).owner); + } + return list; + } + + /** + * Get an array of GWindow objects even if minimised or invisible.
+ * This method never returns null, if there are no open windows the array + * will be of length zero. + * @return an array of references to all open GWindow objects. + */ + public static GWindow[] getOpenWindowsAsArray(){ + ArrayList list = getOpenWindowsAsList(null); + return list.toArray(new GWindow[list.size()]); + } + + /** + * This will open a version of the Java Swing color chooser dialog. The dialog's + * UI is dependent on the OS and JVM implementation running.
+ * + * If you click on Cancel then it returns the last color previously selected. + * + * @return the ARGB colour as a 32 bit integer (as used in Processing). + */ + public static int selectColor(){ + Frame owner = (sketchApplet == null) ? null : sketchApplet.frame; + if(chooser == null){ + chooser = new JColorChooser(); + AbstractColorChooserPanel[] oldPanels = chooser.getChooserPanels(); + // Do not assume what panels are present + LinkedList panels = new LinkedList(); + for(AbstractColorChooserPanel p : oldPanels){ + String displayName = p.getDisplayName().toLowerCase(); + if(displayName.equals("swatches")) + panels.addLast(p); + else if(displayName.equals("rgb")) + panels.addFirst(p); + else if(displayName.startsWith("hs")) + panels.addFirst(p); + } + AbstractColorChooserPanel[] newPanels; + newPanels = panels.toArray(new AbstractColorChooserPanel[panels.size()]); + chooser.setChooserPanels(newPanels); + ColorPreviewPanel pp = new ColorPreviewPanel(lastColor); + chooser.getSelectionModel().addChangeListener(pp); + chooser.setPreviewPanel(pp); + } + // Set the preview color + ((ColorPreviewPanel)chooser.getPreviewPanel()).setPrevColor(lastColor); + // Use the last color selected to start it off + chooser.setColor(lastColor); + JDialog dialog = JColorChooser.createDialog(owner, + "Color picker", + true, + chooser, + new ActionListener() { + public void actionPerformed(ActionEvent e) { + lastColor = chooser.getColor(); + } + }, + null); + dialog.setVisible(true); + return lastColor.getRGB(); + } + + /** + * Select a folder from the local file system. + * + * @param prompt the frame text for the chooser + * @return the absolute path name for the selected folder, or null if action + * cancelled. + */ + public static String selectFolder(String prompt){ + String selectedFolder = null; + Frame frame = (sketchApplet == null) ? null : sketchApplet.frame; + if (PApplet.platform == MACOSX && PApplet.useNativeSelect != false) { + FileDialog fileDialog = + new FileDialog(frame, prompt, FileDialog.LOAD); + System.setProperty("apple.awt.fileDialogForDirectories", "true"); + fileDialog.setVisible(true); + System.setProperty("apple.awt.fileDialogForDirectories", "false"); + String filename = fileDialog.getFile(); + if (filename != null) { + try { + selectedFolder = (new File(fileDialog.getDirectory(), fileDialog.getFile())).getCanonicalPath(); + } catch (IOException e) { + selectedFolder = null; + } + } + } else { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle(prompt); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int result = fileChooser.showOpenDialog(frame); + if (result == JFileChooser.APPROVE_OPTION) { + try { + selectedFolder = fileChooser.getSelectedFile().getCanonicalPath(); + } catch (IOException e) { + selectedFolder = null; + } + } + } + return selectedFolder; + } + + /** + * Select a file for input from the local file system.
+ * + * + * @param prompt the frame text for the chooser + * @return the absolute path name for the selected folder, or null if action + * cancelled. + */ + public static String selectInput(String prompt){ + return selectInput(prompt, null, null); + } + + /** + * Select a file for input from the local file system.
+ * + * This version allows the dialog window to filter the output based on file extensions. + * This is not available on all platforms, if not then it is ignored.
+ * + * It is definitely available on Linux systems because it uses the standard Swing + * JFileFinder component. + * + * @param prompt the frame text for the chooser + * @param types a comma separated list of file extensions e.g. "png,gif,jpg,jpeg" + * @param typeDesc simple textual description of the file types e.g. "Image files" + * @return the absolute path name for the selected folder, or null if action + * cancelled. + */ + public static String selectInput(String prompt, String types, String typeDesc){ + return selectImpl(prompt, FileDialog.LOAD, types, typeDesc); + } + + /** + * Select a file for output from the local file system.
+ * + * @param prompt the frame text for the chooser + * @return the absolute path name for the selected folder, or null if action is cancelled. + */ + public static String selectOutput(String prompt){ + return selectOutput(prompt, null, null); + } + + /** + * Select a file for output from the local file system.
+ * + * This version allows the dialog window to filter the output based on file extensions. + * This is not available on all platforms, if not then it is ignored.
+ * + * It is definitely available on Linux systems because it uses the standard swing + * JFileFinder component. + * + * @param prompt the frame text for the chooser + * @param types a comma separated list of file extensions e.g. "png,jpf,tiff" + * @param typeDesc simple textual description of the file types e.g. "Image files" + * @return the absolute path name for the selected folder, or null if action + * cancelled. + */ + public static String selectOutput(String prompt, String types, String typeDesc){ + return selectImpl(prompt, FileDialog.SAVE, types, typeDesc); + } + + /** + * The implementation of the select input and output methods. + * @param prompt + * @param mode + * @param types + * @param typeDesc + * @return the absolute path name for the selected folder, or null if action + * cancelled. + */ + private static String selectImpl(String prompt, int mode, String types, String typeDesc) { + // If no initial selection made then use last selection + // Assume that a file will not be selected + String selectedFile = null; + // Get the owner + Frame owner = (sketchApplet == null) ? null : sketchApplet.frame; + // Create a file filter + if (PApplet.useNativeSelect) { + FileDialog dialog = new FileDialog(owner, prompt, mode); + FilenameFilter filter = null; + if(types != null && types.length() > 0){ + filter = new FilenameChooserFilter(types); + dialog.setFilenameFilter(filter); + } + dialog.setVisible(true); + String directory = dialog.getDirectory(); + if(directory != null){ + selectedFile = dialog.getFile(); + if(selectedFile != null){ + try { + selectedFile = (new File(directory, selectedFile)).getCanonicalPath(); + } catch (IOException e) { + selectedFile = null; + } + } + } + } else { + JFileChooser chooser = new JFileChooser(); + chooser.setDialogTitle(prompt); + FileFilter filter = null; + if(types != null && types.length() > 0){ + filter = new FileChooserFilter(types, typeDesc); + chooser.setFileFilter(filter); + } + int result = JFileChooser.ERROR_OPTION; + if (mode == FileDialog.SAVE) { + result = chooser.showSaveDialog(owner); + } else if (mode == FileDialog.LOAD) { + result = chooser.showOpenDialog(owner); + } + if (result == JFileChooser.APPROVE_OPTION) { + try { + selectedFile = chooser.getSelectedFile().getCanonicalPath(); + } catch (IOException e) { + selectedFile = null; + } + } + } + return selectedFile; + } + + /* + + Component parentComponent + The first argument to each showXxxDialog method is always the parent component, which must be a + Frame, a component inside a Frame, or null. If you specify a Frame or Dialog, then the Dialog + will appear over the center of the Frame and follow the focus behavior of that Frame. If you + specify a component inside a Frame, then the Dialog will appear over the center of that component + and will follow the focus behavior of that component's Frame. If you specify null, then the look + and feel will pick an appropriate position for the dialog � generally the center of the screen � and + the Dialog will not necessarily follow the focus behavior of any visible Frame or Dialog. + + The JOptionPane constructors do not include this argument. Instead, you specify the parent frame + when you create the JDialog that contains the JOptionPane, and you use the JDialog + setLocationRelativeTo method to set the dialog position. + Object message + This required argument specifies what the dialog should display in its main area. Generally, you + specify a string, which results in the dialog displaying a label with the specified text. You can + split the message over several lines by putting newline (\n) characters inside the message string. + For example: + + "Complete the sentence:\n \"Green eggs and...\"" + + String title + The title of the dialog. + int optionType + Specifies the set of buttons that appear at the bottom of the dialog. Choose from one of the + following standard sets: DEFAULT_OPTION, YES_NO_OPTION, YES_NO_CANCEL_OPTION, OK_CANCEL_OPTION. + int messageType + This argument determines the icon displayed in the dialog. Choose from one of the following + values: PLAIN_MESSAGE (no icon), ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE. + Icon icon + The icon to display in the dialog. + Object[] options + Generally used to specify the string displayed by each button at the bottom of the dialog. See + Customizing Button Text in a Standard Dialog for more information. Can also be used to specify + icons to be displayed by the buttons or non-button components to be added to the button row. + Object initialValue + Specifies the default value to be selected. + + You can either let the option pane display its default icon or specify the icon using the message + type or icon argument. By default, an option pane created with showMessageDialog displays the + information icon, one created with showConfirmDialog or showInputDialog displays the question + icon, and one created with a JOptionPane constructor displays no icon. To specify that the dialog + display a standard icon or no icon, specify the message type corresponding to the icon you desire. + To specify a custom icon, use the icon argument. The icon argument takes precedence over the + message type; as long as the icon argument has a non-null value, the dialog displays the + specified icon. + */ + + private static String PANE_TEXT_STYLE_MACOS = " @@TITLE@@

@@MESSAGE@@

"; + + private static String PANE_TEXT_STYLE_OTHER = " @@MESSAGE@@ "; + + /** + * Display a simple message dialog window.
+ * + * The actual UI will depend on the platform your application is running on.
+ * + * The message type should be one of the following
+ * G4P.PLAIN, G4P.ERROR, G4P.INFO, G4P.WARNING, G4P.QUERY
+ * + * @param owner the control responsible for this dialog. + * @param message the text to be displayed in the main area of the dialog + * @param title the text to appear in the dialog's title bar. + * @param messageType the message type + */ + public static void showMessage(Object owner, String message, String title, int messageType){ + Frame frame = getFrame(owner); + String m; + if(PApplet.platform == PApplet.MACOSX){ + m = PANE_TEXT_STYLE_MACOS.replaceAll("@@TITLE@@", title); + title = ""; + m = m.replaceAll("@@MESSAGE@@", message); + } + else { + m = PANE_TEXT_STYLE_OTHER.replaceAll("@@MESSAGE@@", message); + } + JOptionPane.showMessageDialog(frame, m, title, messageType); + } + + /** + * Display a simple message dialog window.
+ * + * The actual UI will depend on the platform your application is running on.
+ * + * The message type should be one of the following
+ * G4P.PLAIN, G4P.ERROR, G4P.INFO, G4P.WARNING, G4P.QUERY
+ * + * The option type should be one of the following
+ * G4P.YES_NO, G4P.YES_NO_CANCEL, G4P.OK_CANCEL
+ * + * This method returns a value to indicate which button was clicked. It will be + * one of the following
+ * G4P.OK, G4P.YES, G4P.NO, G4P.CANCEL, G4P.CLOSED
+ * + * Some comments on the returned value:
    + *
  • G4P.OK and G4P.YES have the same integer value so can be used interchangeably.
  • + *
  • G4P.CLOSED maybe returned if the dialog box is closed although on some + * systems G4P.NO or G4P.CANCEL may be returned instead.
  • + *
  • It is better to test for a positive response because they have the same value.
  • + *
  • If you must test for a negative response use !G4P.OK or !G4P.YES
+ * + * @param owner the control responsible for this dialog. + * @param message the text to be displayed in the main area of the dialog + * @param title the text to appear in the dialog's title bar. + * @param messageType the message type + * @param optionType + * @return which button was clicked + */ + public static int selectOption(Object owner, String message, String title, int messageType, int optionType){ + Frame frame = getFrame(owner); + String m; + if(PApplet.platform == PApplet.MACOSX){ + m = PANE_TEXT_STYLE_MACOS.replaceAll("@@TITLE@@", title); + title = ""; + m = m.replaceAll("@@MESSAGE@@", message); + } + else { + m = PANE_TEXT_STYLE_OTHER.replaceAll("@@MESSAGE@@", message); + } + return JOptionPane.showOptionDialog(frame, m, title, optionType, messageType, null, null, null); + } + + /** + * Find the Frame associated with this object. + * + * @param owner the object that is responsible for this message + * @return the frame (if any) that owns this object + */ + private static Frame getFrame(Object owner){ + Frame frame = null; + if(owner instanceof PApplet || owner instanceof GWinApplet) + frame = ((PApplet)owner).frame; + else if(owner instanceof GWindow) + frame = (Frame)owner; + else if(owner instanceof GAbstractControl) + frame = ((GAbstractControl) owner).getPApplet().frame; + return frame; + } + + + + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GAbstractControl.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GAbstractControl.java new file mode 100644 index 0000000..d33fd5f --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GAbstractControl.java @@ -0,0 +1,1080 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + + +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; + +import processing.core.PApplet; +import processing.core.PConstants; +import processing.core.PGraphics; +import processing.core.PGraphicsJava2D; +import processing.core.PImage; +import processing.event.KeyEvent; +import processing.event.MouseEvent; + +/** + * Abstract base class for all GUI controls. + * + * @author Peter Lager + * + */ +public abstract class GAbstractControl implements PConstants, GConstants, GConstantsInternal { + + /* + * INTERNAL USE ONLY + * This holds a reference to the GComponent that currently has the + * focus. + * A component loses focus when another component takes focus with the + * takeFocus() method. The takeFocus method should use focusIsWith.loseFocus() + * before setting its value to the new component. + */ + static GAbstractControl focusIsWith = null; + + /* + * INTERNAL USE ONLY + * Use by the tab manager to move focus between controls + * + */ + static GAbstractControl controlToTakeFocus = null; + + /* + * INTERNAL USE ONLY + * Keeps track of the component the mouse is over so the mouse + * cursor can be changed if we wish. + */ + static GAbstractControl cursorIsOver; + + // Increment to be used if on a GPanel + final static int Z_PANEL = 1024; + + // Components that don't release focus automatically + // i.e. GTextField + final static int Z_STICKY = 0; + + // Components that automatically releases focus when appropriate + // e.g. GButton + final static int Z_SLIPPY = 24; + + // Reference to the PApplet object that owns this control + protected PApplet winApp; + + /* Used to when components overlap */ + protected int z = Z_STICKY; + + // Set to true when mouse is dragging : set false on button released + protected boolean dragging = false; + + protected static float epsilon = 0.001f; + + /** Link to the parent panel (if null then it is on main window) */ + protected GAbstractControl parent = null; + + /* + * A list of child GComponents added to this component + * Created and used by GPanel and GCombo classes + */ + protected LinkedList children = null; + + protected int localColorScheme = G4P.globalColorScheme; + protected int[] palette = null; + protected Color[] jpalette = null; + protected int alphaLevel = G4P.globalAlpha; + + /** Top left position of component in pixels (relative to parent or absolute if parent is null) + * (changed form int data type in V3*/ + protected float x, y; + /** Width and height of component in pixels for drawing background (changed form int data type in V3*/ + protected float width, height; + /** Half sizes reduces programming complexity later */ + protected float halfWidth, halfHeight; + /** The centre of the control */ + protected float cx, cy; + /** The angle to control is rotated (radians) */ + protected float rotAngle; + /** Introduced V3 to speed up AffineTransform operations */ + protected double[] temp = new double[2]; + + // New to V3 components have an image buffer which is only redrawn if + // it has been invalidated + protected PGraphicsJava2D buffer = null; + protected boolean bufferInvalid = true; + + /** Whether to show background or not */ + protected boolean opaque = false; + + // The cursor image when over a control + // This should be set in the controls constructor + protected int cursorOver = HAND; + + /* + * Position over control corrected for any transformation.
+ * [0,0] is top left corner of the control. + * This is used to determine the mouse position over any + * particular control or part of a control. + */ + protected float ox, oy; + + /* Simple tag that can be used by the user */ + public String tag; + + /* Allows user to specify a number for this component */ + public int tagNo; + + /* Is the component visible or not */ + boolean visible = true; + + /* Is the component enabled to generate mouse and keyboard events */ + boolean enabled = true; + + /* + * Is the component available for mouse and keyboard events. + * This is only used internally to prevent user input being + * processed during animation. + * It will preserve enabled and visible flags + */ + boolean available = true; + + /* The object to handle the event */ + protected Object eventHandlerObject = null; + /* The method in eventHandlerObject to execute */ + protected Method eventHandlerMethod = null; + /* the name of the method to handle the event */ + protected String eventHandlerMethodName; + + int registeredMethods = 0; + + /* + * Specify the PImage that contains the image{s} to be used for the button's state.
+ * This image may be a composite of 1 to 3 images tiled horizontally. + * @param img + * @param nbrImages in the range 1 - 3 + */ +// static PImage[] loadImages(PImage img, int nbrImages){ +// if(img == null || nbrImages <= 0 || nbrImages > 3) +// return null; +// PImage[] bimage = new PImage[3]; +// int iw = img.width / nbrImages; +// for(int i = 0; i < nbrImages; i++){ +// bimage[i] = new PImage(iw, img.height, ARGB); +// bimage[i].copy(img, +// i * iw, 0, iw, img.height, +// 0, 0, iw, img.height); +// } +// // If less than 3 images reuse last image in set +// for(int i = nbrImages; i < 3; i++) +// bimage[i] = bimage[nbrImages - 1]; +// return bimage; +// } + +// public static String getFocusName(){ +// if(focusIsWith == null) +// return "null"; +// else +// return focusIsWith.toString(); +// } + + /* + * Base constructor for ALL control ctors. It will set the position and size of the + * control based on controlMode.
+ * Since this is an abstract class it is not possible to use it directly + * + */ + public GAbstractControl(PApplet theApplet, float p0, float p1, float p2, float p3) { + // If this is the first control to be created then theAapplet must be the sketchApplet + if(G4P.sketchApplet == null) + G4P.sketchApplet = theApplet; + winApp = theApplet; + GCScheme.makeColorSchemes(winApp); + setPositionAndSize(p0, p1, p2, p3); + rotAngle = 0; + z = 0; + palette = GCScheme.getColor(localColorScheme); + jpalette = GCScheme.getJavaColor(localColorScheme); + tag = this.getClass().getSimpleName(); + } + + /* + * Calculate all the variables that determine the position and size of the + * control. This depends on
control_mode
+ * + */ + private void setPositionAndSize(float n0, float n1, float n2, float n3){ + switch(G4P.control_mode){ + case CORNER: // (x,y,w,h) + x = n0; y = n1; width = n2; height = n3; + halfWidth = width/2; halfHeight = height/2; + cx = x + halfWidth; cy = y + halfHeight; + break; + case CORNERS: // (x0,y0,x1,y1) + x = n0; y = n1; width = n2 - n0; height = n3 - n1; + halfWidth = width/2; halfHeight = height/2; + cx = x + halfWidth; cy = y + halfHeight; + break; + case CENTER: // (cx,cy,w,h) + cx = n0; cy = n1; width = n2; height = n3; + halfWidth = width/2; halfHeight = height/2; + x = cx - halfWidth; y = cy - halfHeight; + break; + } + } + + /** + * Used internally to enforce minimum size constraints + * + * @param w the new width + * @param h the new height + */ + protected void resize(float w, float h){ + width = w; + height = h; + halfWidth = width/2; + halfHeight = height/2; + switch(G4P.control_mode){ + case CORNER: // (x,y,w,h) + case CORNERS: // (x0,y0,x1,y1) +// width = w; +// height = h; +// halfWidth = width/2; +// halfHeight = height/2; + cx = x + halfWidth; cy = y + halfHeight; + break; + case CENTER: // (cx,cy,w,h) +// width = w; +// height = h; +// halfWidth = width/2; +// halfHeight = height/2; + x = cx - halfWidth; y = cy - halfHeight; + break; + } + } + + /* + * These are empty methods to enable polymorphism + */ + public void draw(){} + public void mouseEvent(MouseEvent event){ } + public void keyEvent(KeyEvent e) { } + public void pre(){ } + public void post(){ } + + /** + * This will remove all references to this control in the library.
+ * The user is responsible for nullifying all references to this control + * in their sketch code.
+ * Once this method is called the control cannot be reused but resources + * used by the control remain until all references to the control are + * set to null. + * + */ + public void dispose(){ + G4P.removeControl(this); + } + + /** + * This is for emergency use only!!!!
+ * In this version of the library a visual controls is drawn to off-screen buffer + * and then drawn to the screen by copying the buffer. This means that the + * computationally expensive routines needed to draw the control (especially text + * controls) are only done when a change has been noted. This means that single + * changes need not trigger a full redraw to buffer.
+ * It does mean that an error in the library code could result in the buffer not + * being updated after changes. If this happens then in draw() call this method + * on the affected control, and report it as an issue + * here
+ * Thanks + */ + public void forceBufferUpdate(){ + bufferInvalid = true; + } + + protected HotSpot[] hotspots = null; + protected int currSpot = -1; + + /** + * Stop when we are over a hotspot.
+ * Hotspots should be listed in order of importance. + * + * @param px + * @param py + * @return the index for the first hotspot containing px,py else return -1 + */ + protected int whichHotSpot(float px, float py){ + if(hotspots == null) return -1; + int hs = -1; + for(int i = 0; i < hotspots.length; i++){ + if(hotspots[i].contains(px, py)){ + hs = hotspots[i].id; + break; + } + } + return hs; + } + + protected int getCurrHotSpot(){ + return currSpot; + } + + /** + * Determines if a particular pixel position is over the panel. + * + * @return true if the position is over. + */ + public boolean isOver(float x, float y){ + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + return (currSpot >= 0); + } + + /** + * Set the local colour scheme for this control. Children are ignored. + * + * @param cs the colour scheme to use + */ + public void setLocalColorScheme(int cs){ + cs = Math.abs(cs) % 16; // Force into valid range + if(localColorScheme != cs || palette == null){ + localColorScheme = cs; + palette = GCScheme.getColor(localColorScheme); + jpalette = GCScheme.getJavaColor(localColorScheme); + bufferInvalid = true; + } + } + + /** + * Set the local colour scheme for this control. Children are ignored. + * If required include the children and their children. + * + * @param cs the colour scheme to use + * @param includeChildren if do do the same for all descendants + */ + public void setLocalColorScheme(int cs, boolean includeChildren){ + cs = Math.abs(cs) % 16; // Force into valid range + if(localColorScheme != cs || palette == null){ + localColorScheme = cs; + palette = GCScheme.getColor(localColorScheme); + jpalette = GCScheme.getJavaColor(localColorScheme); + bufferInvalid = true; + if(includeChildren && children != null){ + for(GAbstractControl c : children) + c.setLocalColorScheme(cs, true); + } + } + } + + /** + * Get the local color scheme ID number. + * + */ + public int getLocalColorScheme(){ + return localColorScheme; + } + + /** + * Set the transparency of the component and make it unavailable to + * mouse and keyboard events if below the threshold. Child controls + * are ignored? + * + * @param alpha value in the range 0 (transparent) to 255 (opaque) + */ + public void setAlpha(int alpha){ + alpha = Math.abs(alpha) % 256; + if(alphaLevel != alpha){ + alphaLevel = alpha; + available = (alphaLevel >= ALPHA_BLOCK); + bufferInvalid = true; + } + } + + /** + * Set the transparency of the component and make it unavailable to + * mouse and keyboard events if below the threshold. Child controls + * are ignored?
+ * If required include the children and their children. + * + * @param alpha value in the range 0 (transparent) to 255 (opaque) + * @param includeChildren if do do the same for all descendants + */ + public void setAlpha(int alpha, boolean includeChildren){ + setAlpha(alpha); +// alpha = Math.abs(alpha) % 256; +// alphaLevel = alpha; +// available = (alphaLevel >= ALPHA_BLOCK); + if(includeChildren && children != null){ + for(GAbstractControl c : children) + c.setAlpha(alpha, true); + } + } + + /** + * Get the parent control. If null then this is a top-level component + */ + public GAbstractControl getParent() { + return parent; + } + + /** + * Get the PApplet that manages this component + */ + public PApplet getPApplet() { + return winApp; + } + + protected PGraphics getBuffer(){ + return buffer; + } + + /** + * This method should be used sparingly since it is heavy on resources. + * + * @return a PGraphics object showing current state of the control (ignoring rotation) + */ + public PGraphics getSnapshot(){ + if(buffer != null){ + updateBuffer(); + PGraphicsJava2D snap = (PGraphicsJava2D) winApp.createGraphics(buffer.width, buffer.height, PApplet.JAVA2D); + snap.beginDraw(); + snap.image(buffer,0,0); + return snap; + } + return null; + } + + /* + * Empty method at the moment make abstract + * in final version + */ + protected void updateBuffer() {} + + + /** + * Attempt to create the default event handler for the component class. + * The default event handler is a method that returns void and has a single + * parameter of the same type as the component class generating the + * event and a method name specific for that class. + * + * @param handlerObj the object to handle the event + * @param methodName the method to execute in the object handler class + * @param param_classes the parameter classes. + * @param param_names that names of the parameters (used for error messages only) + */ + @SuppressWarnings("rawtypes") + protected void createEventHandler(Object handlerObj, String methodName, Class[] param_classes, String[] param_names){ + try{ + eventHandlerMethod = handlerObj.getClass().getMethod(methodName, param_classes ); + eventHandlerObject = handlerObj; + eventHandlerMethodName = methodName; + } catch (Exception e) { + GMessenger.message(MISSING, new Object[] {this, methodName, param_classes, param_names}); + eventHandlerObject = null; + } + } + + /** + * Attempt to create the default event handler for the component class. + * The default event handler is a method that returns void and has a single + * parameter of the same type as the component class generating the + * event and a method name specific for that class. + * + * @param obj the object to handle the event + * @param methodName the method to execute in the object handler class + */ + public void addEventHandler(Object obj, String methodName){ + try{ + eventHandlerObject = obj; + eventHandlerMethodName = methodName; + eventHandlerMethod = obj.getClass().getMethod(methodName, new Class[] {this.getClass(), GEvent.class } ); + } catch (Exception e) { + GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class[] { this.getClass(), GEvent.class } } ); + eventHandlerObject = null; + eventHandlerMethodName = ""; + } + } + + /** + * Attempt to fire an event for this component. + * + * The method called must have a single parameter which is the object + * firing the event. + * If the method to be called is to have different parameters then it should + * be overridden in the child class + * The method + */ + protected void fireEvent(Object... objects){ + if(eventHandlerMethod != null){ + try { + eventHandlerMethod.invoke(eventHandlerObject, objects); + } catch (Exception e) { + GMessenger.message(EXCP_IN_HANDLER, + new Object[] {eventHandlerObject, eventHandlerMethodName, e } ); + } + } + } + + /** + * Set the rotation to apply when displaying this control. The center of + * rotation is determined by the control_mode attribute. + * + * @param angle clockwise angle in radians + */ + public void setRotation(float angle){ + setRotation(angle, G4P.control_mode); + } + + /** + * Set the rotation to apply when displaying this control. The center of + * rotation is determined by the mode parameter parameter. + * + * @param angle clockwise angle in radians + * @param mode PApplet.CORNER / CORNERS / CENTER + */ + public void setRotation(float angle, GControlMode mode){ + rotAngle = angle; + AffineTransform aff = new AffineTransform(); + aff.setToRotation(angle); + switch(mode){ + case CORNER: + case CORNERS: + // Rotate about top corner + temp[0] = halfWidth; + temp[1] = halfHeight; + aff.transform(temp, 0, temp, 0, 1); + cx = (float)temp[0] + x;// - halfWidth; + cy = (float)temp[1] + y;// - halfHeight; + break; + case CENTER: + default: + // Rotate about centre + temp[0] = -halfWidth; + temp[1] = -halfHeight; + aff.transform(temp, 0, temp, 0, 1); + x = cx + (float)temp[0]; + y = cy + (float)temp[1]; // should this be minus?? I don't think so + break; + } + } + + /** + * Move the control to the given position based on the mode.
+ * + * The position is not constrained to the screen area.
+ * + * The current control mode determines whether we move the + * corner or the center of the control to px,py
+ * + * @param px the horizontal position to move to + * @param py the vertical position to move to + */ + public void moveTo(float px, float py){ + moveTo(px, py, G4P.control_mode); + } + + /** + * Move the control to the given position based on the mode.
+ * + * Unlike when dragged the position is not constrained to the + * screen area.
+ * + * The mode determines whether we move the corner or the center + * of the control to px,py
+ * + * @param px the horizontal position to move to + * @param py the vertical position to move to + * @param mode the control mode + */ + public void moveTo(float px, float py, GControlMode mode){ + GAbstractControl p = parent; + if(p != null){ + px -= p.width/2; + py -= p.height/2; + } + switch(mode){ + case CORNER: + case CORNERS: + cx += (px - x); + cy += (py - y); + x = cx - width/2; + y = cy - height/2; + break; + case CENTER: + cx = px; + cy = py; + x = cx - width/2; + y = cy - height/2; + break; + } + } + + /** + * Get the left position of the control.
+ * If the control is on a panel then the value returned is relative to the + * top-left corner of the panel otherwise it is relative to the sketch + * window display.
+ * + */ + public float getX() { + if(parent != null) + return x + parent.width/2; + else + return x; + } + + /** + * Get the top position of the control.
+ * If the control is on a panel then the value returned is relative to the + * top-left corner of the panel otherwise it is relative to the sketch + * window display.
+ * + */ + public float getY() { + if(parent != null) + return y + parent.height/2; + else + return y; + } + + /** + * Get the centre x position of the control.
+ * If the control is on a panel then the value returned is relative to the + * top-left corner of the panel otherwise it is relative to the sketch + * window display.
+ */ + public float getCX() { + if(parent != null) + return x + (parent.width + width)/2; + else + return cx; + } + + /** + * Get the centre y position of the control.
+ * If the control is on a panel then the value returned is relative to the + * top-left corner of the panel otherwise it is relative to the sketch + * window display.
+ * + */ + public float getCY() { + if(parent != null) + return x + (parent.width + width)/2; + else + return cy; + } + + /** + * @return the width + */ + public float getWidth() { + return width; + } + + /** + * @return the height + */ + public float getHeight() { + return height; + } + + /** + * + * @param visible the visibility to set + */ + public void setVisible(boolean visible) { + // If we are making it invisible and it has focus give up the focus + if(!visible && focusIsWith == this) + loseFocus(null); + this.visible = visible; + // Only available if + available = visible; + // If this control has children than make them available if this control + // is visible and unavailable if invisible + if(children != null){ + for(GAbstractControl c : children) + c.setAvailable(this.visible); + } + } + + + /** + * @return the component's visibility + */ + public boolean isVisible() { + return visible; + } + + /** + * The availability flag is used by the library code to determine whether + * a control should be considered for drawing and mouse/key input.
+ * It perits an internal control that does not affect the visible + * and enabled state of the control, which are set by the programmer. + * + * If a control and its children are made unavailable it will still be drawn + * but it not respond to user input. + * + * @param avail + */ + protected void setAvailable(boolean avail){ + available = avail; + if(children != null){ + for(GAbstractControl c : children) + c.setAvailable(avail); + } + } + + /** + * Is this control available? + */ + protected boolean isAvailable(){ + return available; + } + + /** + * Determines whether to show the back colour or not. + * Only applies to some components + * @param opaque + */ + public void setOpaque(boolean opaque){ + // Ensure that we dont't go from true >> false otherwise + // it will validate an invalid buffer + bufferInvalid |= (opaque != this.opaque); + this.opaque = opaque; + } + + /** + * Find out if the component is opaque + * @return true if the background is visible + */ + public boolean isOpaque(){ + return opaque; + } + + public boolean isDragging(){ + return dragging; + } + + /** + * Enable or disable the ability of the component to generate mouse events.
+ * GTextField - it also controls key press events
+ * GPanel - controls whether the panel can be moved/collapsed/expanded
+ * @param enable true to enable else false + */ + public void setEnabled(boolean enable){ + enabled = enable; + if(children != null){ + for(GAbstractControl c : children) + c.setEnabled(enable); + } + } + + /** + * Is this component enabled + * @return true if the component is enabled + */ + public boolean isEnabled(){ + return enabled; + } + + /** + * Give the focus to this component but only after allowing the + * current component with focus to release it gracefully.
+ * Always cancel the keyFocusIsWith irrespective of the component + * type. If the component needs to retain keyFocus then override this + * method in that class e.g. GCombo + */ + protected void takeFocus(){ + if(focusIsWith != null && focusIsWith != this) + focusIsWith.loseFocus(this); + focusIsWith = this; + } + + /** + * For most components there is nothing to do when they loose focus. + * Override this method in classes that need to do something when + * they loose focus eg TextField + */ + protected void loseFocus(GAbstractControl grabber){ + if(cursorIsOver == this) + cursorIsOver = null; + focusIsWith = grabber; + } + + /** + * Determines whether this component is to have focus or not + * @param focus + */ + public void setFocus(boolean focus){ + if(focus) + takeFocus(); + else + loseFocus(null); + } + + /** + * Does this component have focus + * @return true if this component has focus else false + */ + public boolean hasFocus(){ + return (this == focusIsWith); + } + + /** + * Get the Z order value for the object with focus. + */ + protected static int focusObjectZ(){ + return (focusIsWith == null) ? -1 : focusIsWith.z; + } + + /** + * This will set the rotation of the control to angle overwriting + * any previous rotation set. Then it calculates the centre position + * so that the original top left corner of the control will be the + * position indicated by x,y with respect to the top left corner of + * parent.
+ * + * The added control will have its position calculated relative to the + * centre of the parent control.
+ * + * All overloaded methods call this one.
+ * + * @param c the control to add. + * @param x the leftmost or centre position depending on controlMode + * @param y the topmost or centre position depending on controlMode + * @param angle the rotation angle (replaces any the angle specified in control) + */ + public void addControl(GAbstractControl c, float x, float y, float angle){ + // Ignore if children are not allowed. + if(children == null) return; + c.rotAngle = angle; + // In child control reset the control so it centred about the origin + AffineTransform aff = new AffineTransform(); + aff.setToRotation(angle); + /* + * The following code should result in the x,y and cx,cy coordinates of + * the added control (c) added being measured relative to the centre of + * this control. + */ + switch(G4P.control_mode){ + case CORNER: + case CORNERS: + // Rotate about top corner + c.x = x; c.y = y; + c.temp[0] = c.halfWidth; + c.temp[1] = c.halfHeight; + aff.transform(c.temp, 0, c.temp, 0, 1); + c.cx = (float)c.temp[0] + x - halfWidth; + c.cy = (float)c.temp[1] + y - halfHeight; + c.x = c.cx - c.halfWidth; + c.y = c.cy - c.halfHeight; + break; + case CENTER: + // Rotate about centre + c.cx = x; c.cy = y; + c.temp[0] = -c.halfWidth; + c.temp[1] = -c.halfHeight; + aff.transform(c.temp, 0, c.temp, 0, 1); + c.x = c.cx + (float)c.temp[0] - halfWidth; + c.y = c.cy - (float)c.temp[1] - halfHeight; + c.cx -= halfWidth; + c.cy -= halfHeight; + break; + } + c.rotAngle = angle; + // Add to parent + c.parent = this; + c.setZ(z); + // Parent will now be responsible for drawing + c.registeredMethods &= (ALL_METHOD - DRAW_METHOD); + if(children == null) + children = new LinkedList(); + children.addLast(c); + Collections.sort(children, new Z_Order()); + // Does the control being added have to do anything extra + c.addToParent(this); + } + + /** + * Add a control at the given position with zero rotation angle. + * + * @param c the control to add. + * @param x the leftmost or centre position depending on controlMode + * @param y the topmost or centre position depending on controlMode + */ + public void addControl(GAbstractControl c, float x, float y){ + if(children == null) return; + addControl(c, x, y, 0); + } + + /** + * Add a control at the position and rotation specified in the control. + * + * @param c the control to add + */ + public void addControl(GAbstractControl c){ + if(children == null) return; + switch(G4P.control_mode){ + case CORNER: + case CORNERS: + addControl(c, c.x, c.y, c.rotAngle); + break; + case CENTER: + addControl(c, c.cx, c.cy, c.rotAngle); + break; + } + } + + public void addControls(GAbstractControl... controls){ + if(children == null) return; + for(GAbstractControl c : controls){ + switch(G4P.control_mode){ + case CORNER: + case CORNERS: + addControl(c, c.x, c.y, c.rotAngle); + break; + case CENTER: + addControl(c, c.cx, c.cy, c.rotAngle); + break; + } + } + } + + /** + * Changes that need to be made to child when added + * + * @param p the parent + */ + protected void addToParent(GAbstractControl p){ + } + + /** + * Get the shape type when the cursor is over a control + * @return shape type + */ + public int getCursorOver() { + return cursorOver; + } + + /** + * Set the shape type to use when the cursor is over a control + * @param cursorOver the shape type to use + */ + public void setCursorOver(int cursorOver) { + this.cursorOver = cursorOver; + } + + /** + * Get an affine transformation that is the compound of all + * transformations including parents + * @param aff + */ + protected AffineTransform getTransform(AffineTransform aff){ + if(parent != null) + aff = parent.getTransform(aff); + aff.translate(cx, cy); + aff.rotate(rotAngle); + return aff; + } + + /** + * This method takes a position px, py and calculates the equivalent + * position [ox,oy] as if no transformations have taken place and + * the origin is the top-left corner of the control. + * @param px + * @param py + */ + protected void calcTransformedOrigin(float px, float py){ + AffineTransform aff = new AffineTransform(); + aff = getTransform(aff); + temp[0] = px; temp[1] = py; + try { + aff.inverseTransform(temp, 0, temp, 0, 1); + ox = (float) temp[0] + halfWidth; + oy = (float) temp[1] + halfHeight; + } catch (NoninvertibleTransformException e) { + } + } + + + /** + * Recursive function to set the priority of a component. This + * is used to determine who gets focus when components overlap + * on the screen e.g. when a combobo expands it might cover a button.
+ * It is used where components have childen e.g. GCombo and + * GPaneln + * It is used when a child component is added. + * @param component + * @param parentZ + */ + protected void setZ(int parentZ){ + z += parentZ; + if(children != null){ + for(GAbstractControl c : children){ + c.setZ(parentZ); + } + } + } + + /** + * If the control is permanently no longer required then call + * this method to remove it and free up resources.
+ * The variable identifier used to create this control should + * be set to null.
+ * For example if you want to dispose of a button called + *
btnDoThis
then to remove the button use the + * statements
+	 * btnDoThis.dispose(); 
+ * btnDoThis = null;
+ */ + public void markForDisposal(){ + G4P.removeControl(this); + } + + + public String toString(){ + if(tag == null) + return this.getClass().getSimpleName(); + else + return tag; + } + + /** + * Comparator used for controlling the order components are drawn + * @author Peter Lager + */ + public static class Z_Order implements Comparator { + + public int compare(GAbstractControl c1, GAbstractControl c2) { + if(c1.z != c2.z) + return new Integer(c1.z).compareTo( new Integer(c2.z)); + else + return new Integer((int) -c1.y).compareTo(new Integer((int) -c2.y)); + } + + } // end of comparator class + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GAlign.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GAlign.java new file mode 100644 index 0000000..6a16914 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GAlign.java @@ -0,0 +1,163 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +/** + * This class provides an enumeration that is used to control the alignment + * of text and images. + * + * @author Peter Lager + * + */ +public enum GAlign { + + INVALID ( -1, "INVALID", "Invalid alignment" ), + + // Horizontal alignment constants + LEFT ( 0, "LEFT", "Align left" ), + CENTER ( 1, "CENTER", "Align centre horizontally" ), + RIGHT ( 2, "RIGHT", "Align right" ), + JUSTIFY ( 3, "JUSTIFY", "Justify text" ), + + // Vertical alignment constants + TOP ( 16, "TOP", "Align top" ), + MIDDLE ( 17, "MIDDLE", "Align middle vertically" ), + BOTTOM ( 18, "BOTTOM", "Align bottom" ); + + + /** + * Get an alignment based on its ID number. + * + * @param id the id number for this alignment. + * @return the alignment or INVALID if not found + */ + public static GAlign getFromID(int id){ + switch(id){ + case 0: + return LEFT; + case 1: + return CENTER; + case 2: + return RIGHT; + case 3: + return JUSTIFY; + case 16: + return TOP; + case 17: + return MIDDLE; + case 18: + return BOTTOM; + } + return INVALID; + } + + /** + * Get an alignment based on its alignment text. + * + * @param text the alignment text. + * @return the alignment or INVALID if not found + */ + public static GAlign getFromText(String text){ + text = text.toUpperCase(); + if(text.equals("LEFT")) + return LEFT; + if(text.equals("CENTER")) + return CENTER; + if(text.equals("RIGHT")) + return RIGHT; + if(text.equals("JUSTIFY")) + return JUSTIFY; + if(text.equals("TOP")) + return TOP; + if(text.equals("MIDDLE")) + return MIDDLE; + if(text.equals("BOTTOM")) + return BOTTOM; + return INVALID; + } + + private int alignID; + private String alignText; + private String description; + + /** + * A private constructor to prevent alignments being create outside this class. + * + * @param id + * @param text + * @param desc + */ + private GAlign(int id, String text, String desc ){ + alignID = id; + alignText = text; + description = desc; + } + + /** + * Get the id number associated with this alignment + * @return the ID associated with this alignment + */ + public int getID(){ + return alignID; + } + + /** + * Get the text ID associated with this alignment. + * + * @return alignment text e.g. "RIGHT" + */ + public String getTextID(){ + return alignText; + } + + /** + * Get the description of this alignment + * + * @return e.g. "Align top" + */ + public String getDesc(){ + return description; + } + + /** + * Is this a horizontal alignment constant? + */ + public boolean isHorzAlign(){ + return alignID >= 0 && alignID <= 8; + } + + /** + * Is this a vertical alignment constant? + */ + public boolean isVertAlign(){ + return alignID >= 16; + } + + /** + * Get the alignment text. + */ + public String toString(){ + return alignText; + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GButton.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GButton.java new file mode 100644 index 0000000..8394bfd --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GButton.java @@ -0,0 +1,278 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSrect; +import g4p_controls.StyledString.TextLayoutInfo; + +import java.awt.Graphics2D; +import java.awt.font.TextLayout; +import java.util.LinkedList; + +import processing.core.PApplet; +import processing.core.PGraphicsJava2D; +import processing.event.MouseEvent; + +/** + * This class is the Button component. + * + * The button face can have either text or an image or both just + * pick the right constructor. + * + * Three types of event can be generated :-
+ * PRESSED RELEASED CLICKED
+ * + * To simplify event handling the button only fires off CLICKED events + * if the mouse button is pressed and released over the button face + * (the default behaviour).
+ * + * Using
button1.fireAllEvents(true);
enables the other 2 events + * for button button1. A PRESSED event is created if the mouse button + * is pressed down over the button face, the CLICKED event is then generated + * if the mouse button is released over the button face. Releasing the + * button off the button face creates a RELEASED event.
+ * + * The image file can either be a single image which is used for + * all button states, or be a composite of 3 images (tiled horizontally) + * which are used for the different button states OFF, OVER and DOWN + * in which case the image width should be divisible by 3.
+ * A number of setImages(...) methods exist to set button state images, these + * can be used once the button is created.
+ * + * + * @author Peter Lager + * + */ +public class GButton extends GTextIconAlignBase { + + // Mouse over status + protected int status = 0; + + // Only report CLICKED events + protected boolean reportAllButtonEvents = false; + + public GButton(PApplet theApplet, float p0, float p1, float p2, float p3) { + this(theApplet, p0, p1, p2, p3, ""); + } + + public GButton(PApplet theApplet, float p0, float p1, float p2, float p3, String text) { + super(theApplet, p0, p1, p2, p3); + // The image buffer is just for the button surface + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.rectMode(PApplet.CORNER); + buffer.g2.setFont(localFont); + hotspots = new HotSpot[]{ + new HSrect(1, 0, 0, width, height) // control surface + }; + setText(text); + z = Z_SLIPPY; + // Now register control with applet + createEventHandler(G4P.sketchApplet, "handleButtonEvents", + new Class[]{ GButton.class, GEvent.class }, + new String[]{ "button", "event" } + ); + registeredMethods = DRAW_METHOD | MOUSE_METHOD; + cursorOver = HAND; + G4P.addControl(this); + } + + /** + * If the parameter is true all 3 event types are generated, if false + * only CLICKED events are generated (default behaviour). + * @param all + */ + public void fireAllEvents(boolean all){ + reportAllButtonEvents = all; + } + + /** + * Enable or disable the ability of the component to generate mouse events.
+ * If the control is to be disabled when it is clicked then this will guarentee the + * mouse offf button image is used. + * @param enable true to enable else false + */ + public void setEnabled(boolean enable){ + super.setEnabled(enable); + if(!enable) + status = OFF_CONTROL; + } + + /** + * + * When a button is clicked on a GButton it generates 3 events (in this order) + * mouse down, mouse up and mouse clicked.
+ * You can test for a particular event type with PRESSED, RELEASED:
+ *
+	 * 	void handleButtonEvents(GButton button) {
+	 *	  if(button == btnName && button.eventType == GButton.PRESSED){
+	 *        // code for button click event
+	 *    }
+	 * 

+ * Where
btnName
is the GButton identifier (variable name)

+ * + * If you only wish to respond to button click events then use the statement
+ *
btnName.fireAllEvents(false); 

+ * This is the default mode. + */ + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + + if(currSpot >= 0 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(focusIsWith != this && currSpot >= 0 && z > focusObjectZ()){ + dragging = false; + status = PRESS_CONTROL; + takeFocus(); + if(reportAllButtonEvents) + fireEvent(this, GEvent.PRESSED); + bufferInvalid = true; + } + break; + case MouseEvent.CLICK: + // No need to test for isOver() since if the component has focus + // and the mouse has not moved since MOUSE_PRESSED otherwise we + // would not get the Java MouseEvent.MOUSE_CLICKED event + if(focusIsWith == this){ + status = OFF_CONTROL; + bufferInvalid = true; + loseFocus(null); + dragging = false; + fireEvent(this, GEvent.CLICKED); + } + break; + case MouseEvent.RELEASE: + // if the mouse has moved then release focus otherwise + // MOUSE_CLICKED will handle it + if(focusIsWith == this && dragging){ + if(reportAllButtonEvents) + fireEvent(this, GEvent.RELEASED); + dragging = false; + loseFocus(null); + status = OFF_CONTROL; + bufferInvalid = true; + } + break; + case MouseEvent.MOVE: + int currStatus = status; + // If dragged state will stay as PRESSED + if(currSpot >= 0) + status = OVER_CONTROL; + else + status = OFF_CONTROL; + if(currStatus != status) + bufferInvalid = true; + break; + case MouseEvent.DRAG: + dragging = (focusIsWith == this); + break; + } + } + + public void draw(){ + if(!visible) return; + + // Update buffer if invalid + updateBuffer(); + winApp.pushStyle(); + + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + winApp.popMatrix(); + winApp.popStyle(); + } + + protected void updateBuffer(){ + if(bufferInvalid) { + Graphics2D g2d = buffer.g2; + // Get the latest lines of text + LinkedList lines = stext.getLines(g2d); + bufferInvalid = false; + buffer.beginDraw(); + // Back ground colour + switch(status){ + case OVER_CONTROL: + buffer.background(palette[6]); + break; + case PRESS_CONTROL: + buffer.background(palette[14]); + break; + default: + buffer.background(palette[4]); + } + g2d.setColor(jpalette[3]); + g2d.setStroke(pen_1_0); + g2d.drawRect(0, 0, (int)width-1, (int)height-1); + // Calculate text and icon placement + calcAlignment(); + // If there is an icon draw it + if(iconW != 0) + buffer.image(bicon[status], siX, siY); + float wrapWidth = stext.getWrapWidth(); + float sx = 0, tw = 0; + buffer.translate(stX, stY); + for(TextLayoutInfo lineInfo : lines){ + TextLayout layout = lineInfo.layout; + buffer.translate(0, layout.getAscent()); + switch(textAlignH){ + case CENTER: + tw = layout.getVisibleAdvance(); + tw = (tw > wrapWidth) ? tw - wrapWidth : tw; + sx = (wrapWidth - tw)/2; + break; + case RIGHT: + tw = layout.getVisibleAdvance(); + tw = (tw > wrapWidth) ? tw - wrapWidth : tw; + sx = wrapWidth - tw; + break; + case LEFT: + case JUSTIFY: + default: + sx = 0; + } + // display text + g2d.setColor(jpalette[2]); + lineInfo.layout.draw(g2d, sx, 0); + buffer.translate(0, layout.getDescent() + layout.getLeading()); + } + buffer.endDraw(); + } + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GCScheme.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GCScheme.java new file mode 100644 index 0000000..6438314 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GCScheme.java @@ -0,0 +1,138 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.awt.Color; +import java.io.IOException; +import java.io.InputStream; + +import processing.core.PApplet; +import processing.core.PImage; + +/** + * Defines a number of color schemes for the GUI components.
+ * + * It loads an image file with all the colors used by the various colour schemes.
+ * It will search for a file for a user defined scheme (user_gui_palette.png) and + * if it can't find it it will use the library default scheme (default_gui_palette.png). + * + * @author Peter Lager + * + */ +public class GCScheme implements GConstants { + + private static int[][] palettes = null; + private static Color[][] jpalettes = null; + + /** + * Set the color scheme to one of the preset schemes + * BLUE / GREEN / RED / PURPLE / YELLOW / CYAN / BROWN + * or if you have created your own schemes following the instructions + * at gui4processing.lagers.org.uk/colorscheme.html then you can enter + * the appropriate numeric value of the scheme. + * + * @param schemeNo + * @return the color scheme based on the scheme number + */ + public static int[] getColor(int schemeNo){ + schemeNo = Math.abs(schemeNo) % 16; + return palettes[schemeNo]; + } + + /** + * Set the color scheme to one of the preset schemes + * BLUE / GREEN / RED / PURPLE / YELLOW / CYAN / BROWN + * or if you have created your own schemes following the instructions + * at gui4processing.lagers.org.uk/colorscheme.html then you can enter + * the appropriate numeric value of the scheme. + * + * @param schemeNo + * @return the color scheme based on the scheme number + */ + public static Color[] getJavaColor(int schemeNo){ + schemeNo = Math.abs(schemeNo) % 16; + return jpalettes[schemeNo]; + } + + /** + * Called every time we create a control. The palettes will be made when + * the first control is created. + * + * @param app + */ + public static void makeColorSchemes(PApplet app) { + // If the palettes have not been created then create them + // otherwise do nothing + if(palettes != null) + return; + // Load the image + PImage image = null;; + InputStream is = app.createInput("user_gui_palette.png"); + if(is != null){ + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + image = app.loadImage("user_gui_palette.png"); + GMessenger.message(USER_COL_SCHEME, null); + } + else { + // User image not provided + image = app.loadImage("default_gui_palette.png"); + } + // Now make the palletes + palettes = new int[16][16]; + jpalettes = new Color[16][16]; + for(int p = 0; p <16; p++) + for(int c = 0; c < 16; c++){ + int col = image.get(c * 16 + 8, p * 16 + 8); + palettes[p][c] = col; + jpalettes[p][c] = new Color((col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff); + } + } + + /** + * This method is called by the G4P GUI Builder tool when there is no + * sketch = no PApplet object to use + */ + public static void makeColorSchemes() { + // If the palettes have not been created then create them + // otherwise do nothing + if(palettes != null) + return; + // Load the image + PImage image = new PImage((new javax.swing.ImageIcon(new GCScheme().getClass().getResource("/data/default_gui_palette.png"))).getImage()); + // Now make the palletes + palettes = new int[16][16]; + jpalettes = new Color[16][16]; + for(int p = 0; p <16; p++) + for(int c = 0; c < 16; c++){ + int col = image.get(c * 16 + 8, p * 16 + 8); + palettes[p][c] = col; + jpalettes[p][c] = new Color((col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff); + } + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GCheckbox.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GCheckbox.java new file mode 100644 index 0000000..ef77386 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GCheckbox.java @@ -0,0 +1,86 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import processing.core.PApplet; + +/** + * A two-state toggle control.
+ * + * GCheckbox objects (also known as tick boxes) are two-state toggle switches that are + * used independently of other tick boxes. + * + * @author Peter Lager + * + */ +public class GCheckbox extends GToggleControl { + + /** + * Create an option button without text. + * + * @param theApplet that will display the control + * @param p0 + * @param p1 + * @param p2 + * @param p3 + */ + public GCheckbox(PApplet theApplet, float p0, float p1, float p2, float p3) { + this(theApplet, p0, p1, p2, p3, ""); + } + + /** + * Create an option button with text. + * + * @param theApplet that will display the control + * @param p0 + * @param p1 + * @param p2 + * @param p3 + * @param text text to be displayed + */ + public GCheckbox(PApplet theApplet, float p0, float p1, float p2, float p3, String text) { + super(theApplet, p0, p1, p2, p3); + opaque = false; + setText(text); + setIcon("tick.png", 2, GAlign.LEFT, null); + setTextAlign(GAlign.LEFT, null); + z = Z_SLIPPY; + // Now register control with applet + createEventHandler(G4P.sketchApplet, "handleToggleControlEvents", + new Class[]{ GToggleControl.class, GEvent.class }, + new String[]{ "checkbox", "event" } + ); + registeredMethods = DRAW_METHOD | MOUSE_METHOD; + cursorOver = HAND; + G4P.addControl(this); + } + + /** + * This enforces independent action because this control cannot be added + * to a toggle group + */ + @Override + protected void setToggleGroup(GToggleGroup tg) {} + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GClip.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GClip.java new file mode 100644 index 0000000..8ef0821 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GClip.java @@ -0,0 +1,178 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + The actual code to create the clipbaord, copy and paste were + taken taken from a similar GUI library Interfascia ALPHA 002 -- + http://superstable.net/interfascia/ produced by Brenden Berg + The main change is to provide static copy and paste methods to + separate the clipboard logic from the component logic and provide + global access. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.ClipboardOwner; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; + +/* + * I wanted to implement copying and pasting to the clipboard using static + * methods to simplify the sharing of a single clipboard over all classes. + * The need to implement the ClipboardOwner interface requires an object so + * this class creates an object the first time an attempt to copy or paste + * is used. + * + * All methods are private except copy() and paste() - lostOwnership() + * has to be public because of the Clipboard owner interface. + * + * @author Peter Lager + * + */ + +/** + * Clipboard functionaliy for plain text
+ * + * This provides clipboard functionality for text and is currently only used by the + * GTextField and GTextArea classes. + * + * @author Peter Lager + * + */ +public class GClip implements ClipboardOwner { + + /** + * Static reference to enforce singleton pattern + */ + private static GClip gclip = null; + + /** + * Class attribute to reference the programs clipboard + */ + private Clipboard clipboard = null; + + + /** + * Copy a string to the clipboard + * @param chars + */ + public static boolean copy(String chars){ + if(gclip == null) + gclip = new GClip(); + return gclip.copyString(chars); + } + + /** + * Get a string from the clipboard + * @return the string on the clipboard + */ + public static String paste(){ + if(gclip == null) + gclip = new GClip(); + return gclip.pasteString(); + } + + /** + * Ctor is private so clipboard is only created when a copy or paste is + * attempted and one does not exist already. + */ + private GClip(){ + if(clipboard == null){ + makeClipboardObject(); + } + } + + /** + * If security permits use the system clipboard otherwise create + * our own application clipboard. + */ + private void makeClipboardObject(){ + SecurityManager security = System.getSecurityManager(); + if (security != null) { + try { + security.checkSystemClipboardAccess(); + clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + } catch (SecurityException e) { + clipboard = new Clipboard("Application Clipboard"); + } + } else { + try { + clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + } catch (Exception e) { + // THIS IS DUMB - true but is there another way - I think not + } + } + } + + /** + * Copy a string to the clipboard. If the Clipboard has not been created + * then create it. + * @return true for a successful copy to clipboard + */ + private boolean copyString(String chars){ + if(clipboard == null) + makeClipboardObject(); + if(clipboard != null){ + StringSelection fieldContent = new StringSelection (chars); + clipboard.setContents (fieldContent, this); + return true; + } + return false; + } + + /** + * Gets a string from the clipboard. If there is no Clipboard + * then create it. + * @return if possible the string on the clipboard else an empty string + */ + private String pasteString(){ + // If there is no clipboard then there is nothing to paste + if(clipboard == null){ + makeClipboardObject(); + return ""; + } + // We have a clipboard so get the string if we can + Transferable clipboardContent = clipboard.getContents(this); + + if ((clipboardContent != null) && + (clipboardContent.isDataFlavorSupported(DataFlavor.stringFlavor))) { + try { + String tempString; + tempString = (String) clipboardContent.getTransferData(DataFlavor.stringFlavor); + return tempString; + } + catch (Exception e) { + e.printStackTrace (); + } + } + return ""; + } + + /** + * Reqd by ClipboardOwner interface + */ + public void lostOwnership(Clipboard clipboard, Transferable contents) { + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GConstants.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GConstants.java new file mode 100644 index 0000000..5e3cdb9 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GConstants.java @@ -0,0 +1,190 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-09 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.awt.font.TextAttribute; + +import javax.swing.JOptionPane; + +/** + * + * These constants can all be available to the Processor. + * + * @author Peter Lager + * + */ +public interface GConstants { + + int RED_SCHEME = 0; + int GREEN_SCHEME = 1; + int YELLOW_SCHEME = 2; + int PURPLE_SCHEME = 3; + int ORANGE_SCHEME = 4; + int CYAN_SCHEME = 5; + int BLUE_SCHEME = 6; + int GOLD_SCHEME = 7; + int SCHEME_8 = 8; + int SCHEME_9 = 9; + int SCHEME_10 = 10; + int SCHEME_11 = 11; + int SCHEME_12 = 12; + int SCHEME_13 = 13; + int SCHEME_14 = 14; + int SCHEME_15 = 15; + + // Keyboard values not covered by Processing + char HOME = java.awt.event.KeyEvent.VK_HOME; + char END = java.awt.event.KeyEvent.VK_END; + + + // Configuration constants + // GRoundControl + int CTRL_ANGULAR = 0x00000501; + int CTRL_HORIZONTAL = 0x00000502; + int CTRL_VERTICAL = 0x00000503; + + // GWindow + int EXIT_APP = 0x00000f01; + int CLOSE_WINDOW = 0x00000f02; + int KEEP_OPEN = 0x00000f03; + + // ### GUI build constants ### + int USER_COL_SCHEME = 0x00010102; + + // The min alpha level for a control to respond to mouse and keyboard + int ALPHA_BLOCK = 128; + // The min alpha before a pixel is considered for a hot spot + int ALPHA_PICK = 48; + + // ### Scroll bar policy constants ### + /** Do not create or display any scrollbars for the text control. */ + int SCROLLBARS_NONE = 0x0000; + /** Create and display vertical scrollbar only. */ + int SCROLLBARS_VERTICAL_ONLY = 0x0001; + /** Create and display horizontal scrollbar only. */ + int SCROLLBARS_HORIZONTAL_ONLY = 0x0002; + /** Create and display both vertical and horizontal scrollbars. */ + int SCROLLBARS_BOTH = 0x0003; + /** whether to hide when not required */ + int SCROLLBARS_AUTOHIDE = 0x1000; + + // ### Scroll bar type constants ### + /** Create and display vertical scrollbar only. */ + int SCROLLBAR_VERTICAL = 1; + /** Create and display horizontal scrollbar only. */ + int SCROLLBAR_HORIZONTAL = 2; + + + // Slider / numeric display types + int INTEGER = 0; + int DECIMAL = 1; + int EXPONENT = 2; + + // Text orientation for sliders + int ORIENT_LEFT = -1; + int ORIENT_TRACK = 0; + int ORIENT_RIGHT = 1; + + // Stick mode + int X4 = 1; + int X8 = 2; + + // Modal dialog messages + // Message types + int PLAIN = JOptionPane.PLAIN_MESSAGE; + int ERROR = JOptionPane.ERROR_MESSAGE; + int INFO = JOptionPane.INFORMATION_MESSAGE; + int WARNING = JOptionPane.WARNING_MESSAGE; + int QUERY = JOptionPane.QUESTION_MESSAGE; + + // Option types + int YES_NO = JOptionPane.YES_NO_OPTION; + int YES_NO_CANCEL = JOptionPane.YES_NO_CANCEL_OPTION; + int OK_CANCEL = JOptionPane.OK_CANCEL_OPTION; + + // Replies to option types + int OK = JOptionPane.OK_OPTION; + int YES = JOptionPane.YES_OPTION; // Has same int value as OK + int NO = JOptionPane.NO_OPTION; + int CANCEL = JOptionPane.CANCEL_OPTION; + int CLOSED = JOptionPane.CLOSED_OPTION; + + // Attribute:- fontface Value Type:- String font family name e.g. "Times New Roman" + TextAttribute FAMILY = TextAttribute.FAMILY; + + // Attribute:- font weight Value Type:- Float in range (0.5 to 2.75) + TextAttribute WEIGHT = TextAttribute.WEIGHT; + // Predefined constants for font weight + Float WEIGHT_EXTRA_LIGHT = new Float(0.5f); + Float WEIGHT_LIGHT = new Float(0.75f); + Float WEIGHT_DEMILIGHT = new Float(0.875f); + Float WEIGHT_REGULAR = new Float(1.0f); + Float WEIGHT_SEMIBOLD = new Float(1.25f); + Float WEIGHT_MEDIUM = new Float(1.5f); + Float WEIGHT_DEMIBOLD = new Float(1.75f); + Float WEIGHT_BOLD = new Float(2.0f); + Float WEIGHT_HEAVY = new Float(2.25f); + Float WEIGHT_EXTRABOLD = new Float(2.5f); + Float WEIGHT_ULTRABOLD = new Float(2.75f); + + // Attribute:- font width Value Type:- Float in range (0.75 to 1.5) + TextAttribute WIDTH = TextAttribute.WIDTH; + // Predefined constants for font width + Float WIDTH_CONDENSED = new Float(0.75f); + Float WIDTH_SEMI_CONDENSED = new Float(0.875f); + Float WIDTH_REGULAR = new Float(1.0f); + Float WIDTH_SEMI_EXTENDED = new Float(1.25f); + Float WIDTH_EXTENDED = new Float(1.5f); + + // Attribute:- font posture Value Type:- Float in range (0.0 to 0.20) + TextAttribute POSTURE = TextAttribute.POSTURE; + // Predefined constants for font posture (plain or italic) + Float POSTURE_REGULAR = new Float(0.0f); + Float POSTURE_OBLIQUE = new Float(0.20f); + + // Attribute:- font size Value Type:- Float + TextAttribute SIZE = TextAttribute.SIZE; + + // Attribute:- font superscript Value Type:- Integer (1 : super or -1 subscript) + TextAttribute SUPERSCRIPT = TextAttribute.SUPERSCRIPT; + // Predefined constants for font super/subscript + Integer SUPERSCRIPT_SUPER = new Integer(1); + Integer SUPERSCRIPT_SUB = new Integer(-1); + Integer SUPERSCRIPT_OFF = new Integer(0); + + // Attribute:- font foreground snd bsckground colour Value Type:- Color + TextAttribute FOREGROUND = TextAttribute.FOREGROUND; + TextAttribute BACKGROUND = TextAttribute.BACKGROUND; + + // Attribute:- font strike through Value:- Boolean + TextAttribute STRIKETHROUGH = TextAttribute.STRIKETHROUGH; + // Predefined constants for font strike through on/off + Boolean STRIKETHROUGH_ON = new Boolean(true); + Boolean STRIKETHROUGH_OFF = new Boolean(false); + + // TextAttribute JUSTIFICATION = TextAttribute.JUSTIFICATION; + // Float JUSTIFICATION_FULL = new Float(1.0f); + // Float JUSTIFICATION_NONE = new Float(0.0f); + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GConstantsInternal.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GConstantsInternal.java new file mode 100644 index 0000000..2e7bd5f --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GConstantsInternal.java @@ -0,0 +1,94 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.awt.BasicStroke; + +/** + * Constants that are used internally by the library. + * + * @author Peter Lager + * + */ +interface GConstantsInternal { + + // Constants for GCustomSlider styles + String SLIDER_STYLES = "|grey_blue|blue18px|green_red20px|purple18px|red_yellow18px|"; + String DEFAULT_SLIDER_STYLE = "grey_blue"; + + // Constants for the control methods + int DRAW_METHOD = 0x00000001; + int MOUSE_METHOD = 0x00000002; + int PRE_METHOD = 0x00000004; + int KEY_METHOD = 0x00000008; + int POST_METHOD = 0x00000010; + int ALL_METHOD = 0x0000001f; + + // ### Error MessageTypes ### + int RUNTIME_ERROR = 0xf0000000; + // Event method handler errors + int MISSING = 0x01000001; // Can't find standard handler + int NONEXISTANT = 0x01000002; + int EXCP_IN_HANDLER = 0x81000003; // Exception in event handler + + // Button/slider status values + int OFF_CONTROL = 0; + int OVER_CONTROL = 1; + int PRESS_CONTROL = 2; + int DRAG_CONTROL = 3; + + // The tint color used when controls are drawn with transparency + int TINT_FOR_ALPHA = 255; + + // Constants for merging attribute runs + int I_NONE = 0; + int I_TL = 1; + int I_TR = 2; + int I_CL = 4; + int I_CR = 8; + int I_INSIDE = 16; + int I_COVERED = 32; + int I_MODES = 63; + + // Merger action + int MERGE_RUNS = 256; + int CLIP_RUN = 512; + int COMBI_MODES = 768; + + // merger decision grid + int[][] grid = new int[][] { + { I_NONE, I_TL, I_CL, I_COVERED, I_COVERED }, + { I_NONE, I_NONE, I_INSIDE, I_INSIDE, I_COVERED }, + { I_NONE, I_NONE, I_INSIDE, I_INSIDE, I_CR }, + { I_NONE, I_NONE, I_NONE, I_NONE, I_TR }, + { I_NONE, I_NONE, I_NONE, I_NONE, I_NONE } + }; + + // Basic strokes needed when using the Graphics2D object for drawing on the buffer + BasicStroke pen_1_0 = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + BasicStroke pen_2_0 = new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + BasicStroke pen_3_0 = new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + BasicStroke pen_4_0 = new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GControlMode.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GControlMode.java new file mode 100644 index 0000000..f0d9895 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GControlMode.java @@ -0,0 +1,26 @@ +package g4p_controls; + +import processing.core.PApplet; +import processing.core.PConstants; + +public enum GControlMode implements PConstants{ + + CORNER ( "X Y W H coordinates", "CORNER", PApplet.CORNER ), + CORNERS ( "X0 Y0 X1 Y1 coordinates", "CORNERS", PApplet.CORNERS ), + CENTER ( "X Y W H coordinates", "CENTER", PApplet.CENTER ); + + + public final String description; + public final String ps_name; + public final int mode; + + private GControlMode(String desc, String name, int ctrl_mode ){ + description = desc; + ps_name = name; + mode = ctrl_mode; + } + + public String toString(){ + return description; + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GCustomSlider.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GCustomSlider.java new file mode 100644 index 0000000..299a453 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GCustomSlider.java @@ -0,0 +1,383 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSalpha; +import g4p_controls.HotSpot.HSrect; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.io.File; + +import processing.core.PApplet; +import processing.core.PGraphics; +import processing.core.PGraphicsJava2D; +import processing.core.PImage; + +/** + * Slider that can be customised with user provided graphics.
+ * + * This class replaces the GWSlider provided in pre v3 editions of this library. + *

+ * The main difference to the GSlider class is the ability to skin the slider with user provided graphics. + * The library provides a number of skins ready for use. You specify the skin to use when the slider is created + * and if the library is unable to load the skin it will print a warning and load the default skin instead.

+ *

Library skins available

+ *
    + *
  • grey_blue (default skin)
  • + *
  • green_red20px
  • + *
  • red_yellow18px
  • + *
  • blue18px
  • + *
  • purple18px
  • + *
+ * A skin requires 5 image files for different parts of the slider which must be stored in their own + * folder (the folder name is also used as the skin name) and this folder should be place inside the + * sketch's data folder.

+ *

The image files have specific names. + *

    + *
  • Left end cap of the slider(end_left.???)
  • + *
  • Right end cap of the slider(end_right.???)
  • + *
  • An extendible centre segment(centre.???)
  • + *
  • Draggable thumb (handle.??? and handle_mouseover.???)
  • + *
+ * Where ??? is the image type file extension. The image type can be any that Processing can handle, the + * most common types will be png, jpg or gif but tga is also permitted

+ * + *

There are very few restrictions about the images you use but when designing the images you should consider + * the following facts:

+ *
    + *
  • the slider will be created to fit the control size (specified in the constructor)
  • + *
  • the horizontal space allocated for the end-caps will be the same for each end (uses the width or the larger end cap image)
  • + *
  • the track width will be the height of the centre image
  • + *
  • the centre image will be tiled along the track length
  • + *
  • the track will be placed in the horizontal and vertical centre of the control.
  • + *
  • the end cap images will be placed in the vertical centre of the control and butted against the track.
  • + *
+ * + * + * @author Peter Lager + * + */ +public class GCustomSlider extends GLinearTrackControl { + + protected PImage leftEnd; + protected PImage thumb; + protected PImage thumb_mouseover; + protected PImage rightEnd; + protected PImage centre; + + /** + * Create a custom slider using the default skin. + * + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + */ + public GCustomSlider(PApplet theApplet, float p0, float p1, float p2, float p3) { + this(theApplet, p0, p1, p2, p3, null); + } + + /** + * Create a custom slider using the skin specified. + * + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + * @param skin the name of the skin (this is also the name of the folder holding the images) + */ + public GCustomSlider(PApplet theApplet, float p0, float p1, float p2, float p3, String skin) { + super(theApplet, p0, p1, p2, p3); + skin = (skin == null) ? "grey_blue" : skin.trim(); + setStyle(skin); + + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + buffer.g2.setFont(G4P.numericLabelFont); + buffer.imageMode(PApplet.CENTER); + hotspots = new HotSpot[]{ + new HSalpha(THUMB_SPOT, width/2 + (parametricPos - 0.5f) * trackLength, height/2, thumb, PApplet.CENTER), // thumb + new HSrect(TRACK_SPOT, (width-trackLength)/2, (height-trackWidth)/2, trackLength, trackWidth), // track + }; + opaque = false; + z = Z_SLIPPY; + + epsilon = 0.98f / trackLength; + ssStartLimit = new StyledString("0.00"); + ssEndLimit = new StyledString("1.00"); + ssValue = new StyledString("0.50"); + + // Now register control with applet + createEventHandler(G4P.sketchApplet, "handleSliderEvents", + new Class[]{ GValueControl.class, GEvent.class }, + new String[]{ "slider", "event" } + ); + registeredMethods = PRE_METHOD | DRAW_METHOD | MOUSE_METHOD; + cursorOver = HAND; + G4P.addControl(this); + } + + /** + * Change the skin used for the slider. + * @param skin the name of the folder holding the graphics for this slider + */ + public void setStyle(String skin){ + loadSkin(skin); + float maxEndLength = Math.max(leftEnd.width, rightEnd.width); + maxEndLength = Math.max(maxEndLength, 10); // make sure we have enough to show limits value + trackLength = Math.round(width - 2 * maxEndLength - TINSET); + trackDisplayLength = trackLength + 2 * Math.min(leftEnd.width, rightEnd.width); + trackWidth = centre.height; + trackOffset = calcTrackOffset(); + extendCentreImage(); + bufferInvalid = true; + } + + /** + * Calculates the amount of offset for the labels + */ + protected float calcTrackOffset(){ + float adjustedTrackOffset = (showTicks) ? trackWidth: trackWidth/2; + adjustedTrackOffset = Math.max(adjustedTrackOffset, thumb.height/2) + 2; + if(adjustedTrackOffset != trackOffset){ + bufferInvalid = true; + } + return adjustedTrackOffset; + } + + + protected void updateDueToValueChanging(){ + hotspots[0].x = (width/2 + (parametricPos - 0.5f) * trackLength); + } + + protected void updateBuffer(){ + if(bufferInvalid) { + Graphics2D g2d = buffer.g2; + bufferInvalid = false; + buffer.beginDraw(); + + // Back ground colour + if(opaque == true) + buffer.background(palette[6]); + else + buffer.background(buffer.color(255,0)); + + // Draw track, thumb, ticks etc. + buffer.pushMatrix(); + buffer.translate(width/2, height/2); + // draw ticks + if(showTicks){ + float delta = 1.0f / (nbrTicks - 1); + for(int i = 0; i < nbrTicks; i++){ + float tickx = Math.round((i * delta - 0.5f) * trackLength); + buffer.strokeWeight(2); + buffer.stroke(255); + buffer.line(tickx+1, -trackWidth, tickx+1, trackWidth); + buffer.strokeWeight(1.0f); + buffer.stroke(0); + buffer.line(tickx, -trackWidth, tickx, trackWidth); + } + } + buffer.image(centre,0,0); + buffer.image(leftEnd, -(trackLength + leftEnd.width)/2, 0); + buffer.image(rightEnd, (trackLength + rightEnd.width)/2, 0); + switch(status){ + case OFF_CONTROL: + buffer.image(thumb,(parametricPos - 0.5f) * trackLength, 0); + break; + case OVER_CONTROL: + case PRESS_CONTROL: + case DRAG_CONTROL: + buffer.image(thumb_mouseover,(parametricPos - 0.5f) * trackLength, 0); + break; + } + // Display slider values + g2d.setColor(jpalette[2]); + if(labels != null){ + drawLabels(); + } + else { + + if(showLimits) + drawLimits(); + if(showValue) + drawValue(); + } + buffer.popMatrix(); + buffer.endDraw(); + } + } + + private void extendCentreImage(){ + int tl = (int)trackLength; + PGraphics pg = winApp.createGraphics(tl, centre.height, JAVA2D); + int rem = tl % centre.width; + int n = tl / centre.width; + n = (rem == 0) ? n : n + 1; + int px = (tl - centre.width * n)/2; + pg.beginDraw(); + pg.background(winApp.color(255,0)); + pg.imageMode(CORNER); + + while(px < tl){ + pg.image(centre, px, 0); + px += centre.width; + } + + pg.endDraw(); + centre = pg; + } + + /** + * Load a skin + * @param style + */ + private void loadSkin(String style){ + // Remember the skin we want to use + String style_used = style; + boolean found = false; + // First check for user defined skin + // See if we are running in a browser or running locally + if(winApp.sketchPath("").length() == 0) + found = loadSkin_AppletInBrowser(style); // browser + else + found = loadStyle_FromSketch(style); // local + // If not found load it from the library + if(!found) + style_used = loadStyle_FromG4P(style); + // See if we have had to use a different skin. If true then + // the original skin could not be found so say so + if(!style.equalsIgnoreCase(style_used)) + System.out.println("Unable to load the skin " + style + " using default '" + DEFAULT_SLIDER_STYLE + "' style instead"); + } + + /** + * If no user defined skin has been specified then load a style from the G4P library. + * If the style does not exist it will use the default style. + * @param style + */ + private String loadStyle_FromG4P(String style) { + boolean found = (SLIDER_STYLES.indexOf("|"+style+"|") >= 0); + // If not found use the default grey_blue + if(!found) + style = DEFAULT_SLIDER_STYLE; + + // All the library styles use png graphics + leftEnd = winApp.loadImage(style + "/end_left.png"); + rightEnd = winApp.loadImage(style + "/end_right.png"); + thumb = winApp.loadImage(style +"/handle.png"); + thumb_mouseover = winApp.loadImage(style +"/handle_mouseover.png"); + // will be stretched before use + centre = winApp.loadImage(style + "/centre.png"); + + return style; + } + + /** + * Load a skin when run as an application or run locally. + * + * @param styleFolder + * @param style + * @return true if the style loaded successfully + */ + private boolean loadStyle_FromSketch(String style) { + // First attempt to locate the style inside the sketch or sketch data folders + File styleFolder = new File(winApp.dataPath(style)); + if(!styleFolder.exists()) + styleFolder = new File(winApp.sketchPath(style)); + // If the style is in the sketch then attempt to load the style + // and if successful we are done + if(!styleFolder.exists()) + return false; + + int fcount = 0; + String[] names = new String[] { "centre.", "end_left.", "end_right.", "handle.", "handle_mouseover." }; + PImage[] images = new PImage[names.length]; + File[] fileList = styleFolder.listFiles(); + for(int i = 0; i < names.length; i++){ + for(File f : fileList){ + String filename = f.getName(); + if(filename.startsWith(names[i])){ + images[i] = winApp.loadImage(style + "/" + filename); + fcount ++; + } + } + } + if(fcount != names.length) + return false; + + centre = images[0]; + leftEnd = images[1]; + rightEnd = images[2]; + thumb = images[3]; + thumb_mouseover = images[4]; + return true; + } + + /** + * Load a skin when run as an applet inside a browser. + * + * Note: sketchPath() is null when inside a browswer. + * + * @param style + * @return true if the style loaded successfully + */ + private boolean loadSkin_AppletInBrowser(String style){ + leftEnd = winApp.loadImage(style + "/end_left.png"); + if(leftEnd == null) + leftEnd = winApp.loadImage(style + "/end_left.jpg"); + rightEnd = winApp.loadImage(style + "/end_right.png"); + if(rightEnd == null) + rightEnd = winApp.loadImage(style + "/end_right.jpg"); + thumb = winApp.loadImage(style +"/handle.png"); + if(thumb == null) + thumb = winApp.loadImage(style +"/handle.jpg"); + thumb_mouseover = winApp.loadImage(style +"/handle_mouseover.png"); + if(thumb_mouseover == null) + thumb_mouseover = winApp.loadImage(style +"/handle_mouseover.jpg"); + // will be stretched before use + centre = winApp.loadImage(style + "/centre.png"); + if(centre == null) + centre = winApp.loadImage(style + "/centre.jpg"); + + boolean found = !(leftEnd == null || rightEnd == null || thumb == null || thumb_mouseover == null || centre == null); + + // See if we have problems with the skin files + if(!found){ + System.out.println("Unable to load the skin " + style + " check the "); + System.out.println("skin name used and ensure all the image files are present."); + System.out.println("Reverting to default 'grey_blue' style"); + loadSkin("grey_blue"); + } + return found; + } + + + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GDropList.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GDropList.java new file mode 100644 index 0000000..8127926 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GDropList.java @@ -0,0 +1,417 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSrect; + +import java.awt.Graphics2D; +import java.awt.font.TextLayout; +import java.util.ArrayList; +import java.util.LinkedList; + +import processing.core.PApplet; +import processing.core.PGraphicsJava2D; +import processing.event.MouseEvent; + +/** + * A drop down list component.
+ * + * This replaces the GCombo control in pre V3 editions of this library.
+ * + * The number of items in the list is not restricted but the user can define + * the maximum number of items to be displayed in the drop list. If there are + * too many items to display a vertical scroll bar is provide to scroll through + * all the items. + * + * The vertical size of an individual item is calculated from the overall height + * specified when creating the control.
+ * + * @author Peter Lager + * + */ +public class GDropList extends GTextBase { + + static protected int LIST_SURFACE = 1; + static protected int CLOSED_SURFACE = 2; + + protected static final int FORE_COLOR = 2; + protected static final int BACK_COLOR = 5; + protected static final int ITEM_FORE_COLOR = 3; + protected static final int ITEM_BACK_COLOR = 6; + protected static final int OVER_ITEM_FORE_COLOR = 15; + + + private GScrollbar vsb; + private GButton showList; + + protected String[] items; + protected StyledString[] sitems; + protected StyledString selText; + + protected int selItem = 0; + protected int startItem = 0; + protected int lastOverItem = -1; + protected int currOverItem = lastOverItem; + + + protected int dropListMaxSize = 4; + protected int dropListActualSize = 4; + + protected float itemHeight, buttonWidth; + + protected boolean expanded = false; // make false in release version + + /** + * Create a drop down list component with a list size of 4. + * + * After creating the control use setItems to initialise the list.
+ * + * @param theApplet the applet that will display this component. + * @param p0 + * @param p1 + * @param p2 + * @param p3 + */ + public GDropList(PApplet theApplet, float p0, float p1, float p2, float p3) { + this(theApplet, p0, p1, p2, p3, 4); + } + + /** + * Create a drop down list component with a specified list size. + * + * After creating the control use setItems to initialise the list.
+ * + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + * @param dropListMaxSize the maximum number of element to appear in the drop down list + */ + public GDropList(PApplet theApplet, float p0, float p1, float p2, float p3, int dropListMaxSize) { + super(theApplet, p0, p1, p2, p3); + children = new LinkedList(); + this.dropListMaxSize = Math.max(dropListMaxSize, 3); + itemHeight = height / (dropListMaxSize + 1); // make allowance for selected text at top + + // The image buffer is just for the typing area + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.rectMode(PApplet.CORNER); + + G4P.pushStyle(); + G4P.showMessages = false; + + vsb = new GScrollbar(theApplet, 0, 0, height - itemHeight, 10); + vsb.addEventHandler(this, "vsbEventHandler"); + vsb.setAutoHide(true); + vsb.setVisible(false); + + buttonWidth = 10; + showList = new GButton(theApplet, 0, 0, buttonWidth, itemHeight, ":"); + showList.addEventHandler(this, "buttonShowListHandler"); + + // Do this before we add the button and scrollbar + z = Z_SLIPPY; + // Add the button and scrollbar + G4P.control_mode = GControlMode.CORNER; + addControl(vsb, width, itemHeight + 1, PI/2); + addControl(showList, width - buttonWidth, 0, 0); + + G4P.popStyle(); + + buffer.g2.setFont(localFont); + hotspots = new HotSpot[]{ + new HSrect(LIST_SURFACE, 0, itemHeight+1, width - 11, height - itemHeight - 1), // text list area + new HSrect(CLOSED_SURFACE, 0, 0, width - buttonWidth, itemHeight) // selected text display area + }; + + createEventHandler(G4P.sketchApplet, "handleDropListEvents", + new Class[]{ GDropList.class, GEvent.class }, + new String[]{ "list", "event" } + ); + registeredMethods = DRAW_METHOD | MOUSE_METHOD; + cursorOver = HAND; + G4P.addControl(this); + } + + /** + * Use this to set or change the list of items to appear in the list. If + * you enter an invalid selection index then it is forced into + * the valid range.
+ * Null and empty values in the list will be ignored.
+ * If the list is null then or empty then then no changes are made.
+ * @param list + * @param selected + */ + public void setItems(String[] list, int selected){ + if(list == null) + return; + // Get rid of null or empty strings + ArrayList strings = new ArrayList(); + for(String s : list) + if(s != null && s.length() > 0) + strings.add(s); + list = strings.toArray(new String[strings.size()]); + if(list.length == 0) + return; + // We have at least one item for the droplist + items = list; + sitems = new StyledString[list.length]; + // Create styled strings for display + for(int i = 0; i < list.length; i++) + sitems[i] = new StyledString(list[i]); + // Force selected value into valid range + selItem = PApplet.constrain(selected, 0, list.length - 1); + startItem = (selItem >= dropListMaxSize) ? selItem - dropListMaxSize + 1 : 0; + // Make selected item bold + sitems[selItem].addAttribute(WEIGHT, WEIGHT_BOLD); + // Create separate styled string for display area + selText = new StyledString(this.items[selItem]); + dropListActualSize = Math.min(list.length, dropListMaxSize); + if((list.length > dropListActualSize)){ + float filler = ((float)dropListMaxSize)/list.length; + float value = ((float)startItem)/list.length; + vsb.setValue(value, filler); + vsb.setVisible(false); // make it false + } + bufferInvalid = true; + } + + /** + * Set the currently selected item from the droplist by index position.
+ * Invalid values are ignored. + * + * @param selected + */ + public void setSelected(int selected){ + if(selected >=0 && selected < sitems.length){ + selItem = selected; + startItem = (selItem >= dropListMaxSize) ? selItem - dropListMaxSize + 1 : 0; + for(StyledString s : sitems) + s.clearAllAttributes(); + sitems[selItem].addAttribute(WEIGHT, WEIGHT_BOLD); + selText = new StyledString(this.items[selItem]); + bufferInvalid = true; + } + } + /** + * Get the index position of the selected item + */ + public int getSelectedIndex(){ + return selItem; + } + + /** + * Get the text for the selected item + */ + public String getSelectedText(){ + return items[selItem]; + } + + /** + * Sets the local colour scheme for this control + */ + public void setLocalColorScheme(int cs){ + super.setLocalColorScheme(cs); + if(showList != null) + showList.setLocalColorScheme(localColorScheme); + if(vsb != null) + vsb.setLocalColorScheme(localColorScheme); + } + + /** + * Determines if a particular pixel position is over this control taking + * into account whether it is collapsed or not. + */ + public boolean isOver(float x, float y){ + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + return (!expanded)? currSpot == CLOSED_SURFACE : currSpot == CLOSED_SURFACE | currSpot == LIST_SURFACE; + } + + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + + if(currSpot >= 0 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.CLICK: + // No need to test for isOver() since if the component has focus + // and the mouse has not moved since MOUSE_PRESSED otherwise we + // would not get the Java MouseEvent.MOUSE_CLICKED event + if(focusIsWith == this ){ + loseFocus(null); + vsb.setVisible(false); + expanded = false; + bufferInvalid = true; + // Make sure that we have selected a valid item and that + // it is not the same as before; + if(currOverItem >= 0 && currOverItem != selItem){ + setSelected(currOverItem); + fireEvent(this, GEvent.SELECTED); + } + currOverItem = lastOverItem = -1; + } + break; + case MouseEvent.MOVE: + if(focusIsWith == this){ + if(currSpot == LIST_SURFACE) + currOverItem = startItem + (int)(oy / itemHeight)-1; + //currOverItem = startItem + Math.round(oy / itemHeight) - 1; + else + currOverItem = -1; + // Only invalidate the buffer if the over item has changed + if(currOverItem != lastOverItem){ + lastOverItem = currOverItem; + bufferInvalid = true; + } + } + break; + } + } + + public void draw(){ + if(!visible) return; + updateBuffer(); + + winApp.pushStyle(); + winApp.pushMatrix(); + + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + + winApp.pushMatrix(); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + + winApp.popMatrix(); + + if(children != null){ + for(GAbstractControl c : children) + c.draw(); + } + winApp.popMatrix(); + winApp.popStyle(); + } + + protected void updateBuffer(){ + if(bufferInvalid) { + Graphics2D g2d = buffer.g2; + bufferInvalid = false; + + buffer.beginDraw(); + buffer.background(buffer.color(255,0)); + + buffer.noStroke(); + buffer.fill(palette[BACK_COLOR]); + buffer.rect(0, 0, width, itemHeight); + + if(expanded){ + buffer.fill(palette[ITEM_BACK_COLOR]); + buffer.rect(0,itemHeight, width, itemHeight * dropListActualSize); + } + + float px = TPAD, py; + TextLayout line; + // Get selected text for display + line = selText.getLines(g2d).getFirst().layout; + py = (itemHeight + line.getAscent() - line.getDescent())/2; + + g2d.setColor(jpalette[FORE_COLOR]); + line.draw(g2d, px, py); + + if(expanded){ + g2d.setColor(jpalette[ITEM_FORE_COLOR]); + for(int i = 0; i < dropListActualSize; i++){ + py += itemHeight; + if(currOverItem == startItem + i) + g2d.setColor(jpalette[OVER_ITEM_FORE_COLOR]); + else + g2d.setColor(jpalette[ITEM_FORE_COLOR]); + + line = sitems[startItem + i].getLines(g2d).getFirst().layout; + line.draw(g2d, px, py); + } + } + buffer.endDraw(); + } + } + + /** + * For most components there is nothing to do when they loose focus. + * Override this method in classes that need to do something when + * they loose focus eg TextField + */ + protected void loseFocus(GAbstractControl grabber){ + if(grabber != vsb){ + expanded = false; + vsb.setVisible(false); + bufferInvalid = true; + } + if(cursorIsOver == this) + cursorIsOver = null; + focusIsWith = grabber; + } + + + /** + * This method should not be called by the user. It + * is for internal library use only. + */ + public void vsbEventHandler(GScrollbar scrollbar, GEvent event){ + int newStartItem = Math.round(vsb.getValue() * items.length); + startItem = newStartItem; + bufferInvalid = true; + } + + /** + * This method should not be called by the user. It + * is for internal library use only. + */ + public void buttonShowListHandler(GButton button, GEvent event){ + if(expanded){ + loseFocus(null); + vsb.setVisible(false); + expanded = false; + } + else { + takeFocus(); + vsb.setVisible(items.length > dropListActualSize); + expanded = true; + } + bufferInvalid = true; + } + +} \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GEditableTextControl.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GEditableTextControl.java new file mode 100644 index 0000000..ac21af1 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GEditableTextControl.java @@ -0,0 +1,665 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2013 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.StyledString.TextLayoutHitInfo; +import g4p_controls.StyledString.TextLayoutInfo; + +import java.awt.Font; +import java.awt.font.TextAttribute; +import java.awt.font.TextHitInfo; +import java.awt.geom.GeneralPath; +import java.util.LinkedList; + +import processing.core.PApplet; +import processing.event.KeyEvent; + +/** + * + * This class is the basis for the GTextField and GTextArea classes. + * + * @author Peter Lager + * + */ +public abstract class GEditableTextControl extends GTextBase { + + protected static float HORZ_SCROLL_RATE = 4f; + protected static float VERT_SCROLL_RATE = 8; + + GTabManager tabManager = null; + + protected StyledString defaultText = null; + // The width to break a line + protected int wrapWidth = Integer.MAX_VALUE; + + // The typing area + protected float tx,ty,th,tw; + // Offset to display area + protected float ptx, pty; + // Caret position + protected float caretX, caretY; + + protected boolean keepCursorInView = false; + + protected GeneralPath gpTextDisplayArea; + + // Used for identifying selection and cursor position + protected TextLayoutHitInfo startTLHI = new TextLayoutHitInfo(); + protected TextLayoutHitInfo endTLHI = new TextLayoutHitInfo(); + + // The scrollbars available + protected final int scrollbarPolicy; + protected boolean autoHide = false; + protected GScrollbar hsb, vsb; + + protected GTimer caretFlasher; + protected boolean showCaret = false; + + // Stuff to manage text selections + protected int endChar = -1, startChar = -1, pos = endChar, nbr = 0, adjust = 0; + protected boolean textChanged = false, newline = false, selectionChanged = false; + + /* Is the component enabled to generate mouse and keyboard events */ + boolean textEditEnabled = true; + + public GEditableTextControl(PApplet theApplet, float p0, float p1, float p2, float p3, int scrollbars) { + super(theApplet, p0, p1, p2, p3); + scrollbarPolicy = scrollbars; + autoHide = ((scrollbars & SCROLLBARS_AUTOHIDE) == SCROLLBARS_AUTOHIDE); + caretFlasher = new GTimer(theApplet, this, "flashCaret", 400); + caretFlasher.start(); + opaque = true; + cursorOver = TEXT; + } + + /** + * Give up focus but if the text is only made from spaces + * then set it to null text.
+ * Fire focus events for the GTextField and GTextArea controls + */ + protected void loseFocus(GAbstractControl grabber){ + // If this control has focus then Fire a lost focus event + if(focusIsWith == this) + fireEvent(this, GEvent.LOST_FOCUS); + // Process mouse-over cursor + if(cursorIsOver == this) + cursorIsOver = null; + focusIsWith = grabber; + // If only blank text clear it out allowing default text (if any) to be displayed + if(stext.length() > 0){ + int tl = stext.getPlainText().trim().length(); + if(tl == 0) + stext = new StyledString("", wrapWidth); + } + keepCursorInView = true; + bufferInvalid = true; + } + + /** + * Give the focus to this component but only after allowing the + * current component with focus to release it gracefully.
+ * Always cancel the keyFocusIsWith irrespective of the component + * type. + * Fire focus events for the GTextField and GTextArea controls + */ + protected void takeFocus(){ + // If focus is not yet with this control fire a gets focus event + if(focusIsWith != this){ + // If the focus is with another control then tell + // that control to lose focus + if(focusIsWith != null) + focusIsWith.loseFocus(this); + fireEvent(this, GEvent.GETS_FOCUS); + } + focusIsWith = this; + } + + /** + * Determines whether this component is to have focus or not.
+ * Fire focus events for the GTextField and GTextArea controls + * @param focus + */ + public void setFocus(boolean focus){ + if(!focus){ + loseFocus(null); + return; + } + // Make sure we have some text + if(focusIsWith != this){ + dragging = false; + if(stext == null || stext.length() == 0) + stext = new StyledString(" ", wrapWidth); +// text = stext.getPlainText(); + LinkedList lines = stext.getLines(buffer.g2); + startTLHI = new TextLayoutHitInfo(lines.getFirst(), null); + startTLHI.thi = startTLHI.tli.layout.getNextLeftHit(1); + + endTLHI = new TextLayoutHitInfo(lines.getLast(), null); + int lastChar = endTLHI.tli.layout.getCharacterCount(); + endTLHI.thi = startTLHI.tli.layout.getNextRightHit(lastChar-1); + + calculateCaretPos(endTLHI); + bufferInvalid = true; + } + keepCursorInView = true; + takeFocus(); + } + + /** + * Set the default text for this control. If provided this text will be + * displayed in italic whenever it is empty. + * @param dtext + */ + public void setDefaultText(String dtext){ + if(dtext == null || dtext.length() == 0) + defaultText = null; + else { + defaultText = new StyledString(dtext, wrapWidth); + defaultText.addAttribute(G4P.POSTURE, G4P.POSTURE_OBLIQUE); + } + bufferInvalid = true; + } + + /** + * Get the default text for this control + * @return the default text without styling + */ + public String getDefaultText(){ + return defaultText.getPlainText(); + } + + /** + * Get the text in the control + * @return the text without styling + */ + public String getText(){ + return stext.getPlainText(); + } + + /** + * Get the styled text in the control + * @return the text with styling + */ + public StyledString getStyledText(){ + return stext; + } + + /** + * Get the text that has been selected (highlighted) by the user.
+ * @return the selected text without styling + */ + public String getSelectedText(){ + if(!hasSelection()) + return ""; + TextLayoutHitInfo startSelTLHI; + TextLayoutHitInfo endSelTLHI; + if(endTLHI.compareTo(startTLHI) == -1){ + startSelTLHI = endTLHI; + endSelTLHI = startTLHI; + } + else { + startSelTLHI = startTLHI; + endSelTLHI = endTLHI; + } + int ss = startSelTLHI.tli.startCharIndex + startSelTLHI.thi.getInsertionIndex(); + int ee = endSelTLHI.tli.startCharIndex + endSelTLHI.thi.getInsertionIndex(); + String s = stext.getPlainText().substring(ss, ee); + return s; + } + + /** + * If some text has been selected then set the style. If there is no selection then + * the text is unchanged. + * + * + * @param style + */ + public void setSelectedTextStyle(TextAttribute style, Object value){ + if(!hasSelection()) + return; + TextLayoutHitInfo startSelTLHI; + TextLayoutHitInfo endSelTLHI; + if(endTLHI.compareTo(startTLHI) == -1){ + startSelTLHI = endTLHI; + endSelTLHI = startTLHI; + } + else { + startSelTLHI = startTLHI; + endSelTLHI = endTLHI; + } + int ss = startSelTLHI.tli.startCharIndex + startSelTLHI.thi.getInsertionIndex(); + int ee = endSelTLHI.tli.startCharIndex + endSelTLHI.thi.getInsertionIndex(); + stext.addAttribute(style, value, ss, ee); + + // We have modified the text style so the end of the selection may have + // moved, so it needs to be recalculated. The start will be unaffected. + stext.getLines(buffer.g2); + endSelTLHI.tli = stext.getTLIforCharNo(ee); + int cn = ee - endSelTLHI.tli.startCharIndex; + if(cn == 0) // start of line + endSelTLHI.thi = endSelTLHI.tli.layout.getNextLeftHit(1); + else + endSelTLHI.thi = endSelTLHI.tli.layout.getNextRightHit(cn-1); + bufferInvalid = true; + } + /** + * Clear any styles applied to the selected text. + */ + public void clearStyle(){ + if(!hasSelection()) + return; + TextLayoutHitInfo startSelTLHI; + TextLayoutHitInfo endSelTLHI; + if(endTLHI.compareTo(startTLHI) == -1){ + startSelTLHI = endTLHI; + endSelTLHI = startTLHI; + } + else { + startSelTLHI = startTLHI; + endSelTLHI = endTLHI; + } + int ss = startSelTLHI.tli.startCharIndex + startSelTLHI.thi.getInsertionIndex(); + int ee = endSelTLHI.tli.startCharIndex + endSelTLHI.thi.getInsertionIndex(); + stext.clearAttributes(ss, ee); + + // We have modified the text style so the end of the selection may have + // moved, so it needs to be recalculated. The start will be unaffected. + stext.getLines(buffer.g2); + endSelTLHI.tli = stext.getTLIforCharNo(ee); + int cn = ee - endSelTLHI.tli.startCharIndex; + if(cn == 0) // start of line + endSelTLHI.thi = endSelTLHI.tli.layout.getNextLeftHit(1); + else + endSelTLHI.thi = endSelTLHI.tli.layout.getNextRightHit(cn-1); + bufferInvalid = true; + } + + /** + * Set the font for this control. + * @param font + */ + public void setFont(Font font) { + if(font != null && font != localFont && buffer != null){ + localFont = font; + buffer.g2.setFont(localFont); + stext.getLines(buffer.g2); + ptx = pty = 0; + setScrollbarValues(ptx, pty); + bufferInvalid = true; + } + } + + /** + * Used internally to set the scrollbar values as the text changes. + * + * @param sx + * @param sy + */ + void setScrollbarValues(float sx, float sy){ + if(vsb != null){ + float sTextHeight = stext.getTextAreaHeight(); + if(sTextHeight < th) + vsb.setValue(0.0f, 1.0f); + else + vsb.setValue(sy/sTextHeight, th/sTextHeight); + } + // If needed update the horizontal scrollbar + if(hsb != null){ + float sTextWidth = stext.getMaxLineLength(); + if(stext.getMaxLineLength() < tw) + hsb.setValue(0,1); + else + hsb.setValue(sx/sTextWidth, tw/sTextWidth); + } + } + + /** + * Move caret to home position + * @param currPos the current position of the caret + * @return true if caret moved else false + */ + protected boolean moveCaretStartOfLine(TextLayoutHitInfo currPos){ + if(currPos.thi.getCharIndex() == 0) + return false; // already at start of line + currPos.thi = currPos.tli.layout.getNextLeftHit(1); + return true; + } + + /** + * Move caret to the end of the line that has the current caret position + * @param currPos the current position of the caret + * @return true if caret moved else false + */ + protected boolean moveCaretEndOfLine(TextLayoutHitInfo currPos){ + if(currPos.thi.getCharIndex() == currPos.tli.nbrChars - 1) + return false; // already at end of line + currPos.thi = currPos.tli.layout.getNextRightHit(currPos.tli.nbrChars - 1); + return true; + } + + /** + * Move caret left by one character. + * @param currPos the current position of the caret + * @return true if caret moved else false + */ + protected boolean moveCaretLeft(TextLayoutHitInfo currPos){ + TextHitInfo nthi = currPos.tli.layout.getNextLeftHit(currPos.thi); + if(nthi == null){ + return false; + } + else { + // Move the caret to the left of current position + currPos.thi = nthi; + } + return true; + } + + /** + * Move caret right by one character. + * @param currPos the current position of the caret + * @return true if caret moved else false + */ + protected boolean moveCaretRight(TextLayoutHitInfo currPos){ + TextHitInfo nthi = currPos.tli.layout.getNextRightHit(currPos.thi); + if(nthi == null){ + return false; + } + else { + currPos.thi = nthi; + } + return true; + } + + public void setJustify(boolean justify){ + stext.setJustify(justify); + bufferInvalid = true; + } + + /** + * Sets the local colour scheme for this control + */ + public void setLocalColorScheme(int cs){ + super.setLocalColorScheme(cs); + if(hsb != null) + hsb.setLocalColorScheme(localColorScheme); + if(vsb != null) + vsb.setLocalColorScheme(localColorScheme); + } + + /** + * Find out if some text is selected (highlighted) + * @return true if some text is selected else false + */ + public boolean hasSelection(){ + return (startTLHI.tli != null && endTLHI.tli != null && startTLHI.compareTo(endTLHI) != 0); + } + + /** + * Calculate the caret (text insertion point) + * + * @param tlhi + */ + protected void calculateCaretPos(TextLayoutHitInfo tlhi){ + float temp[] = tlhi.tli.layout.getCaretInfo(tlhi.thi); + caretX = temp[0]; + caretY = tlhi.tli.yPosInPara; + } + + /** + * Determines whether the text can be edited using the keyboard or mouse. It + * still allows the text to be modified by the sketch code.
+ * If text editing is being disabled and the control has focus then it is forced + * to give up that focus.
+ * This might be useful if you want to use a GTextArea control to display large + * amounts of text that needs scrolling (so cannot use a GLabel) but must not + * change e.g. a user instruction guide. + * + * @param enableTextEdit false to disable keyboard input + */ + public void setTextEditEnabled(boolean enableTextEdit){ + // If we are disabling this then make sure it does not have focus + if(enableTextEdit == false && focusIsWith == this){ + loseFocus(null); + } + enabled = enableTextEdit; + textEditEnabled = enableTextEdit; + } + + public void keyEvent(KeyEvent e) { + if(!visible || !enabled || !textEditEnabled || !available) return; + if(focusIsWith == this && endTLHI != null){ + char keyChar = e.getKey(); + int keyCode = e.getKeyCode(); + int keyID = e.getAction(); + boolean shiftDown = e.isShiftDown(); + boolean ctrlDown = e.isControlDown(); + + textChanged = false; + newline = false; + keepCursorInView = true; + + int startPos = pos, startNbr = nbr; + + // Get selection details + endChar = endTLHI.tli.startCharIndex + endTLHI.thi.getInsertionIndex(); + startChar = (startTLHI != null) ? startTLHI.tli.startCharIndex + startTLHI.thi.getInsertionIndex() : endChar; + pos = endChar; + nbr = 0; + adjust = 0; + if(endChar != startChar){ // Have we some text selected? + if(startChar < endChar){ // Forward selection + pos = startChar; nbr = endChar - pos; + } + else if(startChar > endChar){ // Backward selection + pos = endChar; nbr = startChar - pos; + } + } + if(startPos >= 0){ + if(startPos != pos || startNbr != nbr) + fireEvent(this, GEvent.SELECTION_CHANGED); + } + + if(keyID == KeyEvent.PRESS) { + keyPressedProcess(keyCode, keyChar, shiftDown, ctrlDown); + setScrollbarValues(ptx, pty); + } + else if(keyID == KeyEvent.TYPE ){ // && e.getKey() != KeyEvent.CHAR_UNDEFINED && !ctrlDown){ + keyTypedProcess(keyCode, keyChar, shiftDown, ctrlDown); + setScrollbarValues(ptx, pty); + } + if(textChanged){ + changeText(); + fireEvent(this, GEvent.CHANGED); + } + } + } + + // Enable polymorphism. + protected void keyPressedProcess(int keyCode, char keyChar, boolean shiftDown, boolean ctrlDown) { } + + protected void keyTypedProcess(int keyCode, char keyChar, boolean shiftDown, boolean ctrlDown){ } + + + // Only executed if text has changed + protected void changeText(){ + TextLayoutInfo tli; + TextHitInfo thi = null, thiRight = null; + + pos += adjust; + // Force layouts to be updated + stext.getLines(buffer.g2); + + // Try to get text layout info for the current position + tli = stext.getTLIforCharNo(pos); + if(tli == null){ + // If unable to get a layout for pos then reset everything + endTLHI = null; + startTLHI = null; + ptx = pty = 0; + caretX = caretY = 0; + } + else { + int posInLine = pos - tli.startCharIndex; + + // Get some hit info so we can see what is happening + try{ + thiRight = tli.layout.getNextRightHit(posInLine); + } + catch(Exception excp){ + thiRight = null; + } + + if(posInLine <= 0){ // At start of line + thi = tli.layout.getNextLeftHit(thiRight); + } + else if(posInLine >= tli.nbrChars){ // End of line + thi = tli.layout.getNextRightHit(tli.nbrChars - 1); + } + else { // Character in line; + thi = tli.layout.getNextLeftHit(thiRight); + } + + endTLHI.setInfo(tli, thi); + // Cursor at end of paragraph graphic + calculateCaretPos(endTLHI); + + // Is do we have to move cursor to start of next line + if(newline) { ///stext.getWrapWidth() != Integer.MAX_VALUE && caretX > stext.getWrapWidth()){ + if(pos >= stext.length()){ + stext.insertCharacters(pos, " "); + stext.getLines(buffer.g2); + } + moveCaretRight(endTLHI); + calculateCaretPos(endTLHI); + } + // Finish off by ensuring no selection, invalidate buffer etc. + startTLHI.copyFrom(endTLHI); + } + bufferInvalid = true; + } + + /** + * Do not call this directly. A timer calls this method as and when required. + */ + public void flashCaret(GTimer timer){ + showCaret = !showCaret; + } + + /** + * Do not call this method directly, G4P uses it to handle input from + * the horizontal scrollbar. + */ + public void hsbEventHandler(GScrollbar scrollbar, GEvent event){ + keepCursorInView = false; + ptx = hsb.getValue() * (stext.getMaxLineLength() + 4); + bufferInvalid = true; + } + + /** + * Do not call this method directly, G4P uses it to handle input from + * the vertical scrollbar. + */ + public void vsbEventHandler(GScrollbar scrollbar, GEvent event){ + keepCursorInView = false; + pty = vsb.getValue() * (stext.getTextAreaHeight() + 1.5f * stext.getMaxLineHeight()); + bufferInvalid = true; + } + + /** + * Permanently dispose of this control. + */ + public void markForDisposal(){ + if(tabManager != null) + tabManager.removeControl(this); + super.markForDisposal(); + } + + /** + * Save the styled text used by this control to file.
+ * It will also save the start and end position of any text selection. + * + * @param fname the name of the file to use + * @return true if saved successfully else false + */ + public boolean saveText(String fname){ + if(stext == null) + return false; + if(hasSelection()){ + stext.startIdx = startTLHI.tli.startCharIndex + startTLHI.thi.getInsertionIndex(); + stext.endIdx = endTLHI.tli.startCharIndex + endTLHI.thi.getInsertionIndex(); + } + else { + stext.startIdx = stext.endIdx = -1; + } + StyledString.save(winApp, stext, fname); + return true; + } + + /** + * Load the styled string to be used by this control.
+ * It will also restore any text selection saved with the text. + * + * @param fname the name of the file to use + * @return true if loaded successfully else false + */ + public boolean loadText(String fname){ + StyledString ss = StyledString.load(winApp, fname); + if(ss == null) + return false; + setStyledText(ss); + // Now restore any text selection + if(stext.startIdx >=0){ // we have a selection + // Selection starts at ... + startTLHI = new TextLayoutHitInfo(); + startTLHI.tli = stext.getTLIforCharNo(stext.startIdx); + int pInLayout = stext.startIdx - startTLHI.tli.startCharIndex; + if(pInLayout == 0) + startTLHI.thi = startTLHI.tli.layout.getNextLeftHit(1); + else + startTLHI.thi = startTLHI.tli.layout.getNextRightHit(pInLayout - 1); + // Selection ends at ... + endTLHI = new TextLayoutHitInfo(); + endTLHI.tli = stext.getTLIforCharNo(stext.endIdx); + pInLayout = stext.endIdx - endTLHI.tli.startCharIndex; + + if(pInLayout == 0) + endTLHI.thi = endTLHI.tli.layout.getNextLeftHit(1); + else + endTLHI.thi = endTLHI.tli.layout.getNextRightHit(pInLayout - 1); + calculateCaretPos(endTLHI); + } + bufferInvalid = true; + return true; + } + + /** + * Setting the styled text depends on whether this is a GTextField + * or GTextArea object. Either way the stext will be valid after + * this call so we can restore selection. + * + * @param ss + */ + public abstract void setStyledText(StyledString ss); + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GEvent.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GEvent.java new file mode 100644 index 0000000..19b0be5 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GEvent.java @@ -0,0 +1,96 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +/** + * Enumeration of events that can be fired by G4P.
+ * + * GTextField and GTextArea events
+ * CHANGED Text has changed
+ * SELECTION_CHANGED Text selection has changed
+ * ENTERED Enter/return key typed
+ * LOST_FOCUS TextField/Area lost focus
+ * GETS_FOCUS TextField/Area got focus
+ * + * GPanel events
+ * COLLAPSED Control was collapsed
+ * EXPANDED Control was expanded
+ * DRAGGED Control is being dragged
+ * + * Button control events (PRESSED and RELEASED are not fired by default) + * CLICKED Mouse button was clicked
+ * PRESSED Mouse button was pressed
+ * RELEASED Mouse button was released
+ * + * Slider control events events
+ * VALUE_CHANGING Value is changing
+ * VALUE_STEADY Value has reached a steady state
+ * DRAGGING The mouse is being dragged over a component
+ * + * GCheckbox & GOption events
+ * SELECTED ( "Option selected
+ * DESELECTED ( "Option de-selected
+ * + * @author Peter Lager + * + */ +public enum GEvent { + // GTextField and GTextArea events + CHANGED ( "Text has changed" ), + SELECTION_CHANGED ( "Text selection has changed" ), + ENTERED ( "Enter/return key typed" ), + LOST_FOCUS ( "TextField/Area lost focus" ), + GETS_FOCUS ( "TextField/Area got focus" ), + + + // GPanel events + COLLAPSED ( "Control was collapsed" ), + EXPANDED ( "Control was expanded" ), + DRAGGED ( "Control is being dragged" ), + + // Button control events (PRESSED and RELEASED are not fired by default) + CLICKED ( "Mouse button was clicked" ), + PRESSED ( "Mouse button was pressed" ), + RELEASED ( "Mouse button was released" ), + + // Slider control events events + VALUE_CHANGING ( "Value is changing" ), + VALUE_STEADY ( "Value has reached a steady state" ), + DRAGGING ( "The mouse is being dragged over a component "), + + /// GCheckbox & GOption events + SELECTED ( "Option selected" ), + DESELECTED ( "Option de-selected" ); + + + private String description; + + private GEvent(String desc ){ + description = desc; + } + + public String toString(){ + return description; + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GImageButton.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GImageButton.java new file mode 100644 index 0000000..7a46dcd --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GImageButton.java @@ -0,0 +1,316 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSalpha; +import g4p_controls.HotSpot.HSmask; +import processing.core.PApplet; +import processing.core.PImage; +import processing.event.MouseEvent; + +/** + * Buttons create from this class use a number of images to represent it's + * state. This means that buttons can have an irregular and/or discontinuous + * shape.
+ *

Determining the control size

+ * If when creating the button you specify a particular width and height then + * any images that are not the same size will be scaled to fit without regard + * to the original size or aspect ratio.
+ * + * If when creating the button you do not specify the width and height then it + * will use the width and height of the 'off-button' image and assume that all the + * other images are the same size.
+ * + *

The images

+ * The image button needs 1 to 3 image files to represent the button states
+ * OFF mouse is not over button
+ * OVER mouse is over the button
+ * DOWN the mouse is over the button and a mouse button is being pressed.
+ * + * If you only provide one image then this will be used for all states, if you + * provide two then the second image is used for both OVER and DOWN states.

+ * + * If you don't provide a mask file then the button 'hotspot' is represented by any + * non-transparent pixels in the OFF image. If you do provide a mask file then the + * hotspot is defined by any black pixels in the mask image.

+ * + * + * Three types of event can be generated :-
+ * GEvent.PRESSED GEvent.RELEASED GEvent.CLICKED
+ * + * To simplify event handling the button only fires off CLICKED events + * when the mouse button is pressed and released over the button face + * (the default behaviour).
+ * + * Using
button1.fireAllEvents(true);
enables the other 2 events + * for button button1. A PRESSED event is created if the mouse button + * is pressed down over the button face, the CLICKED event is then generated + * if the mouse button is released over the button face. Releasing the + * button off the button face creates a RELEASED event.
+ * + * + * @author Peter Lager + * + */ +public class GImageButton extends GAbstractControl { + + private static PImage[] errImage = null; + + protected PImage[] bimage = null; + protected PImage mask = null; + + protected int status; + protected boolean reportAllButtonEvents = false; + + + /** + * The control size will be set to the size of the image file used for the button OFF state.
+ * There is no alpha mask file.. + * + * @param theApplet + * @param p0 + * @param p1 + * @param fnames an array of up to 3 image filenames to represent the off/over/down state of the button. + */ + public GImageButton(PApplet theApplet, float p0, float p1, String[] fnames) { + this(theApplet, p0, p1, 0, 0, fnames, null); + } + + /** + * The control size will be set to the size of the image file used for the button OFF state.
+ * + * @param theApplet + * @param p0 + * @param p1 + * @param fnames an array of up to 3 image filenames to represent the off/over/down state of the button. + * @param fnameMask the alpha mask filename or null if no mask + */ + public GImageButton(PApplet theApplet, float p0, float p1, String[] fnames, String fnameMask) { + this(theApplet, p0, p1, 0, 0, fnames, fnameMask); + } + + + /** + * Create an image button of the size specified by the parameters.
+ * The images will be resized to fit and there is no alpha mask file. + * + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + * @param fnames an array of up to 3 image filenames to represent the off/over/down state of the button. + */ + public GImageButton(PApplet theApplet, float p0, float p1, float p2, float p3, String[] fnames) { + this(theApplet, p0, p1, p2, p3, fnames, null); + } + + /** + * Create an image button of the size specified by the parameters.
+ * The images will be resized to fit. + * + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + * @param fnames an array of up to 3 image filenames to represent the off/over/down state of the button. + * @param fnameMask the alpha mask filename or null if no mask + */ + public GImageButton(PApplet theApplet, float p0, float p1, float p2, float p3, String[] fnames, String fnameMask) { + super(theApplet, p0, p1, p2, p3); + if(errImage == null) + errImage = ImageManager.loadImage(winApp, new String[] { "err0.png", "err1.png", "err2.png" }); + + //======================================================================== + // First of all load images + // Make sure we have an array of filenames + if(fnames == null || fnames.length == 0) + fnames = new String[] { "err0.png", "err1.png", "err2.png" }; + bimage = ImageManager.loadImage(winApp, fnames); + // There should be 3 images if not use as many as possible, + // duplicating the last one if neccessary + if(bimage.length != 3){ + PImage[] temp = new PImage[3]; + for(int i = 0; i < 3; i++) + temp[i] = bimage[Math.min(i, bimage.length - 1)]; + bimage = temp; + } + // Get mask image if available + if(fnameMask != null) + mask = winApp.loadImage(fnameMask); + //======================================================================== + + + //======================================================================== + // Now decide whether to resize either the images or the button + if(width > 0 && height > 0){ // Resize images + for(int i = 0; i < bimage.length; i++){ + if(bimage[i].width != width || bimage[i].height != height) + bimage[i].resize((int)width, (int)height); + } + if(mask != null && (mask.width != width || mask.height != height)) + mask.resize((int)width, (int)height); + } + else { // resize button + resize(bimage[0].width, bimage[0].height); + } + //======================================================================== + + + //======================================================================== + // Setup the hotspaots + if(mask != null){ // if we have a mask use it for the hot spot + hotspots = new HotSpot[]{ + new HSmask(1, mask) + }; + } + else { // no mask then use alpha channel of the OFF image + hotspots = new HotSpot[]{ + new HSalpha(1, 0, 0, bimage[0], PApplet.CORNER) + }; + } + //======================================================================== + + z = Z_SLIPPY; + // Now register control with applet + createEventHandler(G4P.sketchApplet, "handleButtonEvents", + new Class[]{ GImageButton.class, GEvent.class }, + new String[]{ "button", "event" } + ); + registeredMethods = DRAW_METHOD | MOUSE_METHOD; + cursorOver = HAND; + G4P.addControl(this); + } + + + public void draw(){ + if(!visible) return; + + // Update buffer if invalid + updateBuffer(); + winApp.pushStyle(); + + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(bimage[status], 0, 0); + winApp.popMatrix(); + winApp.popStyle(); + } + + /** + * + * When a button is clicked on a GButton it generates 3 events (in this order) + * mouse down, mouse up and mouse clicked.
+ * You can test for a particular event type with PRESSED, RELEASED:
+ *
+	 * 	void handleButtonEvents(GButton button) {
+	 *	  if(button == btnName && button.eventType == GButton.PRESSED){
+	 *        // code for button click event
+	 *    }
+	 * 

+ * Where
btnName
is the GButton identifier (variable name)

+ * + * If you only wish to respond to button click events then use the statement
+ *
btnName.fireAllEvents(false); 

+ * This is the default mode. + */ + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + if(currSpot >= 0 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(focusIsWith != this && currSpot >= 0 && z > focusObjectZ()){ + dragging = false; + status = PRESS_CONTROL; + takeFocus(); + if(reportAllButtonEvents) + fireEvent(this, GEvent.PRESSED); + } + break; + case MouseEvent.CLICK: + // No need to test for isOver() since if the component has focus + // and the mouse has not moved since MOUSE_PRESSED otherwise we + // would not get the Java MouseEvent.MOUSE_CLICKED event + if(focusIsWith == this){ + status = OFF_CONTROL; + loseFocus(null); + dragging = false; + fireEvent(this, GEvent.CLICKED); + } + break; + case MouseEvent.RELEASE: + // if the mouse has moved then release focus otherwise + // MOUSE_CLICKED will handle it + if(focusIsWith == this && dragging){ + if(currSpot >= 0) + fireEvent(this, GEvent.CLICKED); + else { + if(reportAllButtonEvents){ + fireEvent(this, GEvent.RELEASED); + } + } + dragging = false; + loseFocus(null); + status = OFF_CONTROL; + } + break; + case MouseEvent.MOVE: + // If dragged state will stay as PRESSED + if(currSpot >= 0) + status = OVER_CONTROL; + else + status = OFF_CONTROL; + break; + case MouseEvent.DRAG: + dragging = (focusIsWith == this); + break; + } + } + + /** + * If the parameter is true all 3 event types are generated, if false + * only CLICKED events are generated (default behaviour). + * @param all + */ + public void fireAllEvents(boolean all){ + reportAllButtonEvents = all; + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GImageToggleButton.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GImageToggleButton.java new file mode 100644 index 0000000..8fd4fa3 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GImageToggleButton.java @@ -0,0 +1,342 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSalpha; +import processing.core.PApplet; +import processing.core.PImage; +import processing.event.MouseEvent; + +/** + * Buttons created from this class have 2 or more toggle states. If the number of states + * is N then the button's value will be in the range 0 to N-1. Most toggle buttons will + * have just two states and these have values 0 and 1.
+ * Clicking on the button advances the state by one, restarting at zero after the last + * state.
+ * Each state must have its own 'picture' and the user must supply these as a tiled image + * where the pictures are tiled in 1D or 2D arrangement without 'empty space' around the + * tiles.
+ * If for any reason the library is unable to use the specified graphics then it will + * provide a default two state toggle switch.
+ * It is also possible to provide an over-button image set for when the mouse moves + * over the button - this is optional.
+ * The button control will always be resized to suit the state picture size (tile size).
+ * The mouse is considered to be over the button it its position is over an opaque pixel + * in the state picture. Since transparent pixels are not included then the button shape + * can be different for each state.
+ * + * + * + * Three types of event can be generated :-
+ * GEvent.PRESSED GEvent.RELEASED GEvent.CLICKED
+ * + * To simplify event handling the button only fires off CLICKED events + * when the mouse button is pressed and released over the button face + * (the default behaviour).
+ * + * Using
button1.fireAllEvents(true);
enables the other 2 events + * for button button1. A PRESSED event is created if the mouse button + * is pressed down over the button face, the CLICKED event is then generated + * if the mouse button is released over the button face. Releasing the + * button off the button face creates a RELEASED event. This is included for + * completeness since it is unlikely you will need to detect these events + * for this type of control.
+ * + * + * @author Peter Lager + * + */ +public class GImageToggleButton extends GAbstractControl { + + private static PImage toggle = null; + private static final String TOGGLE = "toggle.png"; + + protected int nbrStates = 2; + protected int stateValue = 0; + + protected PImage[] offImage; + protected PImage[] overImage; + + protected int status; + protected boolean reportAllButtonEvents = false; + + + /** + * Create the library default image-toggle-button at the stated position.
+ * + * @param theApplet + * @param p0 horizontal position of the control + * @param p1 vertical position of the control + */ + public GImageToggleButton(PApplet theApplet, float p0, float p1){ + this(theApplet, p0, p1, null, null, 1, 1); + } + + /** + * Create an image-toggle-button.
+ * Single row of tiles. + * + * @param theApplet + * @param p0 horizontal position of the control + * @param p1 vertical position of the control + * @param offPicture the filename of bitmap containing toggle state pictures + * @param nbrCols number of tiles horizontally + * @param nbrRows number of tiles vertically + */ + public GImageToggleButton(PApplet theApplet, float p0, float p1, String offPicture, int nbrCols){ + this(theApplet, p0, p1, offPicture, null, nbrCols, 1); + } + + /** + * Create an image-toggle-button.
+ * + * @param theApplet + * @param p0 horizontal position of the control + * @param p1 vertical position of the control + * @param offPicture the filename of bitmap containing toggle state pictures + * @param nbrCols number of tiles horizontally + * @param nbrRows number of tiles vertically + */ + public GImageToggleButton(PApplet theApplet, float p0, float p1, String offPicture, int nbrCols, int nbrRows){ + this(theApplet, p0, p1, offPicture, null, nbrCols, nbrRows); + } + + /** + * Create an image-toggle-button.
+ * Single row of tiles. + * + * @param theApplet + * @param p0 horizontal position of the control + * @param p1 vertical position of the control + * @param offPicture the filename of bitmap containing toggle state pictures + * @param overPicture the filename of bitmap containing mouse-over button toggle state pictures + * @param nbrCols number of tiles horizontally + */ + public GImageToggleButton(PApplet theApplet, float p0, float p1, String offPicture, String overPicture, int nbrCols){ + this(theApplet, p0, p1, offPicture, overPicture, nbrCols, 1); + } + + /** + * Create an image-toggle-button.
+ * + * @param theApplet + * @param p0 horizontal position of the control + * @param p1 vertical position of the control + * @param offPicture the filename of bitmap containing toggle state pictures + * @param overPicture the filename of bitmap containing mouse-over button toggle state pictures + * @param nbrCols number of tiles horizontally + * @param nbrRows number of tiles vertically + */ + public GImageToggleButton(PApplet theApplet, float p0, float p1, String offPicture, String overPicture, int nbrCols, int nbrRows){ + super(theApplet, p0, p1, 0, 0); + // Attempt to get off-control image data + PImage temp = null; + if(nbrCols < 1 || nbrRows < 1 || offPicture == null || null == (temp = ImageManager.loadImage(winApp, offPicture))){ + // Invalid data use default + nbrStates = 2; + if(toggle == null) + toggle = ImageManager.loadImage(winApp, TOGGLE); + offImage = ImageManager.makeTiles1D(winApp, toggle, 2, 1); + } + else { + // Off-control image data valid + nbrStates = nbrCols * nbrRows; + offImage = ImageManager.makeTiles1D(winApp, temp, nbrCols, nbrRows); + // Now check for over-control image data + if(overPicture != null && null != (temp = ImageManager.loadImage(winApp, overPicture))){ + overImage = ImageManager.makeTiles1D(winApp, temp, nbrCols, nbrRows); + } + } + // The control will always be resized to match the image size + resize(offImage[0].width, offImage[0].height); + + //======================================================================== + // Setup the hotspots + hotspots = new HotSpot[]{ + new HSalpha(1, 0, 0, offImage[stateValue], PApplet.CORNER) + }; + + //======================================================================== + + z = Z_SLIPPY; + // Now register control with applet + createEventHandler(G4P.sketchApplet, "handleToggleButtonEvents", + new Class[]{ GImageToggleButton.class, GEvent.class }, + new String[]{ "button", "event" } + ); + registeredMethods = DRAW_METHOD | MOUSE_METHOD; + cursorOver = HAND; + G4P.addControl(this); + } + + public void draw(){ + if(!visible) return; + + // Update buffer if invalid + updateBuffer(); + winApp.pushStyle(); + + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + if(status == OVER_CONTROL && overImage != null) + winApp.image(overImage[stateValue], 0, 0); + else + winApp.image(offImage[stateValue], 0, 0); + + winApp.popMatrix(); + winApp.popStyle(); + } + + + /** + * + * When a button is clicked on a GButton it generates 3 events (in this order) + * mouse down, mouse up and mouse clicked.
+ * You can test for a particular event type with PRESSED, RELEASED:
+ *
+	 * 	void handleButtonEvents(GButton button) {
+	 *	  if(button == btnName && button.eventType == GButton.PRESSED){
+	 *        // code for button click event
+	 *    }
+	 * 

+ * Where
btnName
is the GButton identifier (variable name)

+ * + * If you only wish to respond to button click events then use the statement
+ *
btnName.fireAllEvents(false); 

+ * This is the default mode. + */ + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + if(currSpot >= 0 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(focusIsWith != this && currSpot >= 0 && z > focusObjectZ()){ + dragging = false; + status = PRESS_CONTROL; + takeFocus(); + if(reportAllButtonEvents) + fireEvent(this, GEvent.PRESSED); + } + break; + case MouseEvent.CLICK: + // No need to test for isOver() since if the component has focus + // and the mouse has not moved since MOUSE_PRESSED otherwise we + // would not get the Java MouseEvent.MOUSE_CLICKED event + if(focusIsWith == this){ + status = OFF_CONTROL; + loseFocus(null); + dragging = false; + nextState(); + fireEvent(this, GEvent.CLICKED); + } + break; + case MouseEvent.RELEASE: + // if the mouse has moved then release focus otherwise + // MOUSE_CLICKED will handle it + if(focusIsWith == this && dragging){ + if(currSpot >= 0){ + nextState(); + fireEvent(this, GEvent.CLICKED); + } + else { + if(reportAllButtonEvents){ + fireEvent(this, GEvent.RELEASED); + } + } + dragging = false; + loseFocus(null); + status = OFF_CONTROL; + } + break; + case MouseEvent.MOVE: + // If dragged state will stay as PRESSED + if(currSpot >= 0) + status = OVER_CONTROL; + else + status = OFF_CONTROL; + break; + case MouseEvent.DRAG: + dragging = (focusIsWith == this); + break; + } + } + + /** + * Advance to the next state and adjust the hotspot to use the current image + */ + private void nextState(){ + stateValue++; + stateValue %= nbrStates; + hotspots[0].adjust(0,0,offImage[stateValue]); + } + + /** + * Get the current state value of the button. + * @return + */ + public int stateValue(){ + return stateValue; + } + + /** + * Change the current toggle state.
+ * If the parameter is not a valid toggle state value then it + * is ignored and the button's state value is unchanged. + * @param newState + */ + public void stateValue(int newState){ + if(newState >= 0 && newState < nbrStates && newState != stateValue){ + stateValue = newState; + hotspots[0].adjust(0,0,offImage[stateValue]); + bufferInvalid = true; + } + } + + /** + * If the parameter is true all 3 event types are generated, if false + * only CLICKED events are generated (default behaviour).
+ * For this toggle control I can't see the need for anything but + * CLICKED events + * @param all + */ + public void fireAllEvents(boolean all){ + reportAllButtonEvents = all; + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GKnob.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GKnob.java new file mode 100644 index 0000000..4c78c3b --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GKnob.java @@ -0,0 +1,569 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSarc; +import g4p_controls.HotSpot.HScircle; + +import processing.core.PApplet; +import processing.core.PGraphicsJava2D; +import processing.event.MouseEvent; + + +/** + * The provides an extremely configurable GUI knob controller. GKnob + * inherits from GValueControl so you should read the documentation + * for that class as it also applies to GKnob.

+ * + * Configurable options
+ * Knob size but it must be circular
+ * Start and end of rotation arc.
+ * Bezel width with tick marks
+ * User defined value limits (i.e. the range of values returned
+ *
+ * Range of values associated with rotating the knob
+ * Rotation is controlled by mouse movement - 3 modes available
+ * (a) angular - drag round knob center
+ * (b) horizontal - drag left or right
+ * (c) vertical - drag up or down
+ * User can specify mouse sensitivity for modes (b) and (c) + * Use can specify easing to give smoother rotation + * + * Note: Angles are measured clockwise starting in the positive x direction i.e. + *
+ *         270
+ *          |
+ *    180 --+-- 0
+ *          |
+ *          90
+ * 
+ * + * @author Peter Lager + * + */ +public class GKnob extends GValueControl { + + + protected float startAng = 110, endAng = 70; + + protected int mode = CTRL_HORIZONTAL; + + protected boolean showTrack = true; + + protected float bezelRadius, bezelWidth, gripRadius; + protected boolean overIncludesBezel = true; + protected float sensitivity = 1.0f; + + protected boolean drawArcOnly = false; + protected boolean mouseOverArcOnly = false; + + protected float startMouseX, startMouseY; + protected float lastMouseAngle, mouseAngle; + + // corresponds to target and current values + // parametricTarget + protected float angleTarget, lastAngleTarget; + + /** + * Will create the a circular knob control that fits the rectangle define by + * the values passed as parameters.
+ * The knob has two zones the outer bezel and the inner gripper. The radius of + * the outer bezel is calculated from
+ *
bezel radius = min(width, height)/2 - 2

+ * The radius of the inner griper radius is calculated from the bezel radius + * and the last parameter.
+ *
grip radius = bezel radiius * gripAmount 

+ * The gripAmount should be in te range 0.0 to 1.0 inclusive. The actual value + * will be constrained to that range.
+ * + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + * @param gripAmount must be >=0.0 and <=1.0 + */ + public GKnob(PApplet theApplet, float p0, float p1, float p2, float p3, float gripAmount) { + super(theApplet, p0, p1, p2, p3); + bezelRadius = Math.min(width, height) / 2 - 2; + setGripAmount(gripAmount); + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + setTurnRange(startAng, endAng); + // valuePos and valueTarget will start at 0.5; + lastAngleTarget = angleTarget = scaleValueToAngle(parametricTarget); + hotspots = new HotSpot[]{ + new HScircle(1, width/2, height/2, gripRadius) + }; + z = Z_SLIPPY; + + epsilon = 0.98f / (endAng - startAng); + showTicks = true; + + // Now register control with applet + createEventHandler(G4P.sketchApplet, "handleKnobEvents", + new Class[]{ GValueControl.class, GEvent.class }, + new String[]{ "knob", "event" } + ); + registeredMethods = PRE_METHOD | DRAW_METHOD | MOUSE_METHOD ; + cursorOver = HAND; + G4P.addControl(this); + } + + /** + * The radius of the inner griper radius is calculated from the bezel radius + * and the parameter gripAmount using
+ *
grip radius = bezel radiius * gripAmount 

+ * The gripAmount should be in te range 0.0 to 1.0 inclusive. The actual value + * will be constrained to that range.
+ * + * @param gripAmount must be >=0.0 and <=1.0 + */ + public void setGripAmount(float gripAmount){ + gripAmount = PApplet.constrain(gripAmount, 0.0f, 1.0f); + gripRadius = bezelRadius * gripAmount; + if(gripRadius < 2.0f) gripRadius = 0.0f; + bezelWidth = bezelRadius - gripRadius; + bufferInvalid = true; + } + + protected void calculateHotSpot(){ + float overRad = (this.overIncludesBezel) ? bezelRadius : gripRadius; + if(mouseOverArcOnly) + hotspots[0] = new HSarc(1, width/2 , height/2, overRad, startAng, endAng); // over grip + else + hotspots[0] = new HScircle(1, width/2, height/2, overRad); + } + + /** + * For a particular normalised value calculate the angle (degrees) + * + * @param v + * @return the needle angle for the given value + */ + protected float scaleValueToAngle(float v){ + float a = startAng + v * (endAng - startAng); + return a; + } + + /** + * Calculates the knob angle based on the normalised value. + * + * @param a + */ + protected float calcAngletoValue(float a){ + if(a < startAng) + a += 360; + float v = (a - startAng) / (endAng - startAng); + return v; + } + + /** + * Set the value for the slider.
+ * The user must ensure that the value is valid for the slider range. + * @param v + */ + public void setValue(float v){ + super.setValue(v); + angleTarget = scaleValueToAngle(parametricTarget); + } + + /** + * Whether or not to show the circular progress bar. + * @param showTrack true for visible + */ + public void setShowTrack(boolean showTrack){ + if(this.showTrack != showTrack){ + this.showTrack = showTrack; + bufferInvalid = true; + } + } + + /** + * Are we showing the the value track bar. + */ + public boolean isShowTrack(){ + return showTrack; + } + + /** + * Whether to include the bezel when deciding when the mouse is over. + * @param overBezel true if bezel inclded. + */ + public void setIncludeOverBezel(boolean overBezel){ + overIncludesBezel = overBezel; + calculateHotSpot(); + } + + /** + * Is the bezel included when considering when the mouse is over. + * @return true if included. + */ + public boolean isIncludeOverBezel(){ + return overIncludesBezel; + } + + /** + * Decides when the knob will respond to the mouse buttons. If set to true + * it will only respond when ver the arc made by the start and end angles. If + * false it will be the full circle. + * @param arcOnly + */ + public void setOverArcOnly(boolean arcOnly){ + mouseOverArcOnly = arcOnly; + calculateHotSpot(); + } + + /** + * Does the mouse only respond when over the arc? + * @return true = yes + */ + public boolean isOverArcOnly(){ + return mouseOverArcOnly; + } + + /** + * Convenience method to set both the show and the mouse over arc only properties + * for this knob + * @param over_arc_only mouse over arc only? + * @param draw_arc_only draw arc only? + * @param overfullsize include bezel in mouse over calculations? + */ + public void setArcPolicy(boolean over_arc_only, boolean draw_arc_only, boolean overfullsize){ + this.mouseOverArcOnly = over_arc_only; + setShowArcOnly(draw_arc_only); + overIncludesBezel = overfullsize; + calculateHotSpot(); + } + + /** + * This will decide whether the knob is draw as a full circle or as an arc. + * + * @param arcOnly true for arc only + */ + public void setShowArcOnly(boolean arcOnly){ + if(drawArcOnly != arcOnly){ + drawArcOnly = arcOnly; + bufferInvalid = true; + } + } + + /** + * Are we showing arc only? + * @return true = yes + */ + public boolean isShowArcOnly(){ + return drawArcOnly; + } + + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + // Normalise ox and oy to the centre of the knob + ox -= width/2; + oy -= height/2; + + // currSpot == 1 for text display area + if(currSpot >= 0 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(focusIsWith != this && currSpot > -1 && z > focusObjectZ()){ + startMouseX = ox; + startMouseY = oy; + lastMouseAngle = mouseAngle = getAngleFromUser(ox, oy); + offset = scaleValueToAngle(parametricTarget) - mouseAngle; + takeFocus(); + } + break; + case MouseEvent.RELEASE: + if(focusIsWith == this){ + loseFocus(null); + } + // Correct for sticky ticks if needed + if(stickToTicks) + parametricTarget = findNearestTickValueTo(parametricTarget); + dragging = false; + break; + case MouseEvent.DRAG: + if(focusIsWith == this){ + mouseAngle = getAngleFromUser(ox, oy); + if(mouseAngle != lastMouseAngle){ + float deltaMangle = mouseAngle - lastMouseAngle; + // correct when we go over zero degree position + if(deltaMangle < -180) + deltaMangle += 360; + else if(deltaMangle > 180) + deltaMangle -= 360; + // Calculate and adjust new needle angle so it is in the range aLow >>> aHigh + angleTarget = constrainToTurnRange(angleTarget + deltaMangle); + parametricTarget = calcAngletoValue(angleTarget); + // Update offset for use with angular mouse control + offset += (angleTarget - lastAngleTarget - deltaMangle); + // Remember target needle and mouse angles + lastAngleTarget = angleTarget; + lastMouseAngle = mouseAngle; + } + } + break; + } + } + + + + public void draw(){ + if(!visible) return; + // Update buffer if invalid + updateBuffer(); + winApp.pushStyle(); + + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + winApp.pushMatrix(); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + winApp.popMatrix(); + // Value labels + if(children != null){ + for(GAbstractControl c : children) + c.draw(); + } + winApp.popMatrix(); + + winApp.popStyle(); + } + + protected void updateBuffer(){ + double a, sina, cosa; + float tickLength; + if(bufferInvalid) { + bufferInvalid = false; + buffer.beginDraw(); + buffer.ellipseMode(PApplet.CENTER); + // Back ground colour + // Back ground colour + if(opaque == true) + buffer.background(palette[6]); + else + buffer.background(buffer.color(255,0)); + buffer.translate(width/2, height/2); + buffer.noStroke(); + float anglePos = scaleValueToAngle(parametricPos); + if(bezelWidth > 0){ + // Draw bezel, track, ticks etc + buffer.noStroke(); + buffer.fill(palette[5]); + if(drawArcOnly) + buffer.arc(0,0,2*bezelRadius, 2*bezelRadius, PApplet.radians(startAng), PApplet.radians(endAng)); + else + buffer.ellipse(0,0,2*bezelRadius, 2*bezelRadius); + // Since we have a bezel test for ticks + if(showTicks){ + buffer.noFill(); + buffer.strokeWeight(1.6f); + buffer.stroke(palette[3]); + float deltaA = (endAng - startAng)/(nbrTicks - 1); + for(int t = 0; t < nbrTicks; t++){ + tickLength = gripRadius + ((t == 0 || t == nbrTicks - 1) ? bezelWidth : bezelWidth * 0.8f); + a = Math.toRadians(startAng + t * deltaA); + sina = Math.sin(a); + cosa = Math.cos(a); + buffer.line((float)(gripRadius * cosa), (float)(gripRadius * sina), (float)(tickLength * cosa), (float)(tickLength * sina)); + } + } + // draw track? + if(showTrack){ + buffer.noStroke(); + buffer.fill(palette[14]); + buffer.arc(0,0, 2*(gripRadius + bezelWidth * 0.5f), 2*(gripRadius + bezelWidth * 0.5f), PApplet.radians(startAng), PApplet.radians(anglePos)); + } + } + + // draw grip (inner) part of knob + buffer.strokeWeight(1.6f); + buffer.stroke(palette[2]); // was 14 + buffer.fill(palette[2]); + if(drawArcOnly) + buffer.arc(0,0,2*gripRadius, 2*gripRadius, PApplet.radians(startAng), PApplet.radians(endAng)); + else + buffer.ellipse(0,0,2*gripRadius, 2*gripRadius); + + // Draw needle + buffer.noFill(); + buffer.stroke(palette[14]); + buffer.strokeWeight(3); + a = Math.toRadians(anglePos); + sina = Math.sin(a); + cosa = Math.cos(a); + buffer.line(0, 0, (float)(gripRadius * cosa), (float)(gripRadius * sina)); + buffer.endDraw(); + } + } + + + /** + * Get the current mouse controller mode possible values are
+ * GKnob.CTRL_ANGULAR or GKnob.CTRL_HORIZONTAL) orGKnob.CTRL_VERTICAL + * @return the mode + */ + public int getTurnMode() { + return mode; + } + + /** + * Set the mouse control mode to use, acceptable values are
+ * GKnob.CTRL_ANGULAR or GKnob.CTRL_HORIZONTAL) orGKnob.CTRL_VERTICAL any + * other value will be ignored. + * @param mode the mode to set + */ + public void setTurnMode(int mode) { + switch(mode){ + case CTRL_ANGULAR: + case CTRL_HORIZONTAL: + case CTRL_VERTICAL: + this.mode = mode; + } + } + + /** + * This gets the sensitivity to be used in modes CTRL_HORIZONTAL and CTRL_VERTICAL + * @return the sensitivity + */ + public float getSensitivity() { + return sensitivity; + } + + /** + * This gets the sensitivity to be used in modes CTRL_HORIZONTAL and CTRL_VERTICAL
+ * A value of 1 is 1 degree per pixel and a value of 2 is 2 degrees per pixel.
+ * @param sensitivity the sensitivity to set + */ + public void setSensitivity(float sensitivity) { + this.sensitivity = (sensitivity < 0.1f) ? 0.1f : sensitivity; + } + + /** + * Calculates the 'angle' from the current mouse position based on the type + * of 'controller' set. + * @param px the distance from the knob centre in the x direction + * @param py the distance from the knob centre in the y direction + * @return the unconstrained angle + */ + protected float getAngleFromUser(float px, float py){ + float degs = 0; + switch(mode){ + case CTRL_ANGULAR: + degs = calcRealAngleFromXY(ox, oy); + break; + case CTRL_HORIZONTAL: + degs = sensitivity * (px - startMouseX); + break; + case CTRL_VERTICAL: + degs = sensitivity * (py - startMouseY); + break; + } + return degs; + } + + /** + * Set the limits for the range of valid rotation angles for the knob. + * + * @param start the range start angle in degrees + * @param end the range end angle in degrees + */ + public void setTurnRange(float start, float end){ + start = constrain360(start); + end = constrain360(end); + startAng = start; + endAng = (startAng >= end) ? end + 360 : end; + setValue(getValueF()); +// anglePos = angleTarget; + bufferInvalid = true; + } + + /** + * Determines whether an angle is within the knob + * rotation range. + * @param a the angle in degrees + * @return true is angle is within rotation range else false + */ + protected boolean isInTurnRange(float a){ + a = constrain360(a); + if(a < startAng) + a += 360; + return (a >= startAng && a <= endAng); + } + + /** + * Accept an angle and constrain it to the knob angle range. + * + * @param a + * @return the constrained angle + */ + protected float constrainToTurnRange(float a){ + if(a < startAng) + a = startAng; + else if(a > endAng) + a = endAng; + return a; + } + + /** + * Accept an angle and constrain it to the range 0-360 + * @param a + * @return the constrained angle + */ + protected float constrain360(float a){ + while(a < 0) + a += 360; + while(a > 360) + a -= 360; + return a; + } + + /** + * Calculate the angle to the knob centre making sure it is in + * the range 0-360 + * @param px + * @param py + * @return the angle from the knob centre to the specified point. + */ + protected float calcRealAngleFromXY(float px, float py){ + float a = (float) Math.toDegrees(Math.atan2(py, px)); + if(a < 0) + a += 360; + return a; + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GLabel.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GLabel.java new file mode 100644 index 0000000..096225f --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GLabel.java @@ -0,0 +1,148 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.StyledString.TextLayoutInfo; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.TextLayout; +import java.util.LinkedList; + +import processing.core.PApplet; +import processing.core.PGraphicsJava2D; + +/** + * The label component. + * + * This control can display text with/without an icon. + * + * @author Peter Lager + * + */ +public class GLabel extends GTextIconAlignBase { + + public GLabel(PApplet theApplet, float p0, float p1, float p2, float p3) { + this(theApplet, p0, p1, p2, p3, " "); + } + + /** + * Create a label control. + * + * use setIcon to add an icon + * + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + * @param text + */ + public GLabel(PApplet theApplet, float p0, float p1, float p2, float p3, String text) { + super(theApplet, p0, p1, p2, p3); + // The image buffer is just for the typing area + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + buffer.rectMode(PApplet.CORNER); + buffer.g2.setFont(localFont); + setText(text); + opaque = false; + // Now register control with applet + registeredMethods = DRAW_METHOD; + G4P.addControl(this); + } + + public void draw(){ + if(!visible) return; + + // Update buffer if invalid + updateBuffer(); + winApp.pushStyle(); + + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + winApp.popMatrix(); + + winApp.popStyle(); + } + + protected void updateBuffer(){ + if(bufferInvalid) { + Graphics2D g2d = buffer.g2; + // Get the latest lines of text + LinkedList lines = stext.getLines(g2d); + bufferInvalid = false; + buffer.beginDraw(); + // Back ground colour + if(opaque == true) + buffer.background(palette[6]); + else + buffer.background(buffer.color(255,0)); + // Calculate text and icon placement + calcAlignment(); + // If there is an icon draw it + if(iconW != 0) + buffer.image(bicon[0], siX, siY); + float wrapWidth = stext.getWrapWidth(); + float sx = 0, tw = 0; + buffer.translate(stX, stY); + for(TextLayoutInfo lineInfo : lines){ + TextLayout layout = lineInfo.layout; + buffer.translate(0, layout.getAscent()); + switch(textAlignH){ + case CENTER: + tw = layout.getVisibleAdvance(); + tw = (tw > wrapWidth) ? tw - wrapWidth : tw; + sx = (wrapWidth - tw)/2; + break; + case RIGHT: + tw = layout.getVisibleAdvance(); + tw = (tw > wrapWidth) ? tw - wrapWidth : tw; + sx = wrapWidth - tw; + break; + case LEFT: + case JUSTIFY: + default: + sx = 0; + } + // display text + g2d.setColor(jpalette[2]); + lineInfo.layout.draw(g2d, sx, 0); + buffer.translate(0, layout.getDescent() + layout.getLeading()); + } + buffer.endDraw(); + } + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GLinearTrackControl.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GLinearTrackControl.java new file mode 100644 index 0000000..2301ae2 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GLinearTrackControl.java @@ -0,0 +1,421 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.awt.Graphics2D; +import java.awt.font.TextLayout; + +import processing.core.PApplet; +import processing.event.MouseEvent; + +/** + * Base class for linear sliders. + * + * This class provides the ability to control the orientation + * the text should be displayed. It also enables the use of labels + * for each tick mark. + * + * @author Peter Lager + * + */ +public abstract class GLinearTrackControl extends GValueControl { + + static protected float TINSET = 2; + + static protected int THUMB_SPOT = 1; + static protected int TRACK_SPOT = 2; + + protected float trackWidth, trackLength, trackDisplayLength; + protected float trackOffset; + + protected int textOrientation = ORIENT_TRACK; + + protected int downHotSpot = -1; + // Mouse over status + protected int status = OFF_CONTROL; + + // For labels + protected StyledString[] labels; + protected boolean labelsInvalid = true; + + public GLinearTrackControl(PApplet theApplet, float p0, float p1, float p2, float p3) { + super(theApplet, p0, p1, p2, p3); + } + + /** + * Set the text orientation for the display of the limits and value if appropriate.
+ * Acceptable values are G4P.ORIENT_LEFT, G4P.ORIENT_RIGHT or G4P.ORIENT_TRACK
+ * If an invalid value is passed the ORIENT_TRACK is used. + * + * @param orient the orientation of the number labels + */ + public void setTextOrientation(int orient){ + switch(orient){ + case ORIENT_LEFT: + case ORIENT_RIGHT: + case ORIENT_TRACK: + textOrientation = orient; + break; + default: + textOrientation = ORIENT_TRACK; + } + bufferInvalid = true; + } + + /** + * This method is used to set the text to appear alongside the tick marks.
+ * The array passed must have a minimum of 2 elements and each label (element) + * must have at least 1 character. If these two conditions are not met then + * the call to this method will be ignored and no changes are made. + * + * @param tickLabels an array of strings for the labels + */ + public void setTickLabels(String[] tickLabels){ + if(tickLabels == null || tickLabels.length < 2) + return; + for(String s : tickLabels) + if(s == null || s.length() == 0) + return; + labels = new StyledString[tickLabels.length]; + for(int i = 0; i < tickLabels.length; i++) + labels[i] = new StyledString(tickLabels[i]); + stickToTicks = true; + nbrTicks = labels.length; + startLimit = 0; + endLimit = nbrTicks - 1; + valueType = INTEGER; + showLimits = false; + showValue = false; + bufferInvalid = true; + } + + /** + * Set whether the tick marks are to be displayed or not. Then + * recalculate the track offset for the value and limit text. + * @param showTicks the showTicks to set + */ + public void setShowTicks(boolean showTicks) { + super.setShowTicks(showTicks); + float newTrackOffset = calcTrackOffset(); + if(newTrackOffset != trackOffset){ + trackOffset = newTrackOffset; + bufferInvalid = true; + } + bufferInvalid = true; + } + + /** + * Calculates the amount of offset for the labels + */ + protected float calcTrackOffset(){ + return (showTicks) ? trackWidth + 2 : trackWidth/2 + 2; + } + + /** + * The offset is the distance the value/labels are drawn from the + * centre of the track.
+ * You may wish to tweak this value for visual effect. + * @param offset + */ + public void setTrackOffset(float offset){ + trackOffset = offset; + } + + /** + * Get the visual offset for the value/label text. + */ + public float getTrackOffset(){ + return trackOffset; + } + + /** + * If we are using labels then this will get the label text + * associated with the current value.
+ * If labels have not been set then return null + */ + public String getValueS(){ + // Use the valueTarget rather than the valuePos since intermediate values + // have no meaning in this case. + int idx = Math.round(startLimit + (endLimit - startLimit) * parametricTarget); + return (labels == null) ? getNumericDisplayString(getValueF()) : labels[idx].getPlainText(); + } + + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + // Normalise ox and oy to the centre of the slider + ox -= width/2; + ox /= trackLength; + + if(currSpot >= 0 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(focusIsWith != this && currSpot > -1 && z >= focusObjectZ()){ + downHotSpot = currSpot; + status = (downHotSpot == THUMB_SPOT) ? PRESS_CONTROL : OFF_CONTROL; + offset = ox + 0.5f - parametricPos; // normalised + takeFocus(); + bufferInvalid = true; + } + break; + case MouseEvent.CLICK: + if(focusIsWith == this ){ + parametricTarget = ox + 0.5f; + if(stickToTicks) + parametricTarget = findNearestTickValueTo(parametricTarget); + dragging = false; + status = OFF_CONTROL; + loseFocus(null); + bufferInvalid = true; + } + break; + case MouseEvent.RELEASE: + if(focusIsWith == this && dragging){ + if(downHotSpot == THUMB_SPOT){ + parametricTarget = (ox - offset) + 0.5f; + if(parametricTarget < 0){ + parametricTarget = 0; + offset = 0; + } + else if(parametricTarget > 1){ + parametricTarget = 1; + offset = 0; + } + if(stickToTicks) + parametricTarget = findNearestTickValueTo(parametricTarget); + } + status = OFF_CONTROL; + bufferInvalid = true; + loseFocus(null); + } + dragging = false; + break; + case MouseEvent.DRAG: + if(focusIsWith == this){ + status = DRAG_CONTROL; + dragging = true; + if(downHotSpot == THUMB_SPOT){ + parametricTarget = (ox - offset) + 0.5f; + if(parametricTarget < 0){ + parametricTarget = 0; + offset = 0; + } + else if(parametricTarget > 1){ + parametricTarget = 1; + offset = 0; + } + bufferInvalid = true; + } + } + break; + case MouseEvent.MOVE: + int currStatus = status; + // If dragged state will stay as PRESSED + if(currSpot == THUMB_SPOT) + status = OVER_CONTROL; + else + status = OFF_CONTROL; + if(currStatus != status) + bufferInvalid = true; + break; + } + } + + + + public void draw(){ + if(!visible) return; + // Update buffer if invalid + updateBuffer(); + winApp.pushStyle(); + + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + winApp.pushMatrix(); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + winApp.popMatrix(); + winApp.popMatrix(); + + winApp.popStyle(); + } + + protected void drawValue(){ + Graphics2D g2d = buffer.g2; + float px, py; + TextLayout line; + ssValue = new StyledString(getNumericDisplayString(getValueF())); + line = ssValue.getLines(g2d).getFirst().layout; + float advance = line.getVisibleAdvance(); + switch(textOrientation){ + case ORIENT_LEFT: + px = (parametricPos - 0.5f) * trackLength + line.getDescent(); + py = -trackOffset; + buffer.pushMatrix(); + buffer.translate(px, py); + buffer.rotate(-PI/2); + line.draw(g2d, 0, 0 ); + buffer.popMatrix(); + break; + case ORIENT_RIGHT: + px = (parametricPos - 0.5f) * trackLength - line.getDescent(); + py = - trackOffset - advance; + buffer.pushMatrix(); + buffer.translate(px, py); + buffer.rotate(PI/2); + line.draw(g2d, 0, 0 ); + buffer.popMatrix(); + break; + case ORIENT_TRACK: + px = (parametricPos - 0.5f) * trackLength - advance /2; + if(px < -trackDisplayLength/2) + px = -trackDisplayLength/2; + else if(px + advance > trackDisplayLength /2) + px = trackDisplayLength/2 - advance; + py = -trackOffset - line.getDescent(); + line.draw(g2d, px, py ); + line = ssEndLimit.getLines(g2d).getFirst().layout; + break; + } + } + + protected void drawLimits(){ + Graphics2D g2d = buffer.g2; + float px, py; + TextLayout line; + if(limitsInvalid){ + ssStartLimit = new StyledString(getNumericDisplayString(startLimit)); + ssEndLimit = new StyledString(getNumericDisplayString(endLimit)); + limitsInvalid = false; + } + switch(textOrientation){ + case ORIENT_LEFT: + line = ssStartLimit.getLines(g2d).getFirst().layout; + px = -trackLength/2 + line.getDescent(); + py = trackOffset + line.getVisibleAdvance(); + buffer.pushMatrix(); + buffer.translate(px, py); + buffer.rotate(-PI/2); + line.draw(g2d, 0, 0 ); + buffer.popMatrix(); + line = ssEndLimit.getLines(g2d).getFirst().layout; + px = trackLength/2 + line.getDescent(); + py = trackOffset + line.getVisibleAdvance(); + buffer.pushMatrix(); + buffer.translate(px, py); + buffer.rotate(-PI/2); + line.draw(g2d, 0, 0 ); + buffer.popMatrix(); + break; + case ORIENT_RIGHT: + line = ssStartLimit.getLines(g2d).getFirst().layout; + px = -trackLength/2 - line.getDescent(); + py = trackOffset; + buffer.pushMatrix(); + buffer.translate(px, py); + buffer.rotate(PI/2); + line.draw(g2d, 0, 0 ); + buffer.popMatrix(); + line = ssEndLimit.getLines(g2d).getFirst().layout; + px = trackLength/2 - line.getDescent(); + py = trackOffset; + buffer.pushMatrix(); + buffer.translate(px, py); + buffer.rotate(PI/2); + line.draw(g2d, 0, 0 ); + buffer.popMatrix(); + break; + case ORIENT_TRACK: + line = ssStartLimit.getLines(g2d).getFirst().layout; + px = -(trackLength + trackWidth)/2; + py = trackOffset + line.getAscent(); + line.draw(g2d, px, py ); + line = ssEndLimit.getLines(g2d).getFirst().layout; + px = (trackLength + trackWidth)/2 - line.getVisibleAdvance(); + py = trackOffset + line.getAscent(); + line.draw(g2d, px, py ); + break; + } + } + + protected void drawLabels(){ + Graphics2D g2d = buffer.g2; + float px, py; + TextLayout line; + if(labelsInvalid){ + ssStartLimit = new StyledString(getNumericDisplayString(startLimit)); + ssEndLimit = new StyledString(getNumericDisplayString(endLimit)); + limitsInvalid = false; + } + float deltaX = 1.0f / (nbrTicks - 1); + switch(textOrientation){ + case ORIENT_LEFT: + for(int i = 0; i < labels.length; i++){ + line = labels[i].getLines(g2d).getFirst().layout; + px = (i * deltaX - 0.5f)*trackLength + line.getDescent(); + py = trackOffset + line.getVisibleAdvance(); + buffer.pushMatrix(); + buffer.translate(px, py); + buffer.rotate(-PI/2); + line.draw(g2d, 0, 0 ); + buffer.popMatrix(); + } + break; + case ORIENT_RIGHT: + for(int i = 0; i < labels.length; i++){ + line = labels[i].getLines(g2d).getFirst().layout; + px = (i * deltaX - 0.5f)*trackLength - line.getDescent(); + py = trackOffset; + buffer.pushMatrix(); + buffer.translate(px, py); + buffer.rotate(PI/2); + line.draw(g2d, 0, 0 ); + buffer.popMatrix(); + } + break; + case ORIENT_TRACK: + for(int i = 0; i < labels.length; i++){ + line = labels[i].getLines(g2d).getFirst().layout; + px = (i * deltaX - 0.5f)*trackLength - 0.5f * line.getVisibleAdvance(); + py = trackOffset + line.getAscent(); + line.draw(g2d, px, py ); + } + break; + } + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GMessenger.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GMessenger.java new file mode 100644 index 0000000..2e21bec --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GMessenger.java @@ -0,0 +1,155 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-09 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +/** + * CLASS FOR INTERNAL USE ONLY + * + * @author Peter Lager + * + */ +class GMessenger implements GConstants, GConstantsInternal { + + /** + * Display an error message message + * + * @param id message ID number + * @param obj + * @param info + */ + static void message(Integer id, Object[] info){ + // Display G4P messages if required + if(G4P.showMessages){ + switch(id){ + case MISSING: + missingEventHandler(info); + break; + case USER_COL_SCHEME: + System.out.println("USER DEFINED colour schema active"); + break; + } + } + // Display all runtime errors + switch(id){ + case NONEXISTANT: + nonexistantEventHandler(info); + break; + case EXCP_IN_HANDLER: + eventHandlerFailed(info); + break; + } + } + + /** + * Error message when an exception is create inside an event handler. + * @param handler + * @param info + */ + private static void eventHandlerFailed(Object[] info) { + String className = info[0].getClass().getSimpleName(); + String methodName = (String) info[1]; + Exception e = (Exception) info[2]; + Throwable t = e.getCause(); + StackTraceElement[] calls = t.getStackTrace(); + StringBuilder output = new StringBuilder(); + output.append("################ EXCEPTION IN EVENT HANDLER ################"); + output.append("\nAn error occured during execution of the eventhandler:"); + output.append("\nCLASS: "+className+" METHOD: "+methodName); + output.append("\n\tCaused by " + t.toString()); + for(Object line : calls) + output.append("\n\t"+ line.toString()); +// if(calls.length > 0) +// output.append("\n\t"+ calls[0].toString()); + output.append("\n##############################################################\n"); + System.out.println(output); + } + + // GMessenger.message(, this, new Object[] {methodName, param_classes, param_names}); + + /** + * Unable to find the default handler method. + * + * info[0] event generator class + * info[1] event handling method name + * info[2] the parameter class names + * info[3] the parameter names (identifiers) + * + * @param event_generator the object creating the events + * @param info method signature information. + */ + private static void missingEventHandler(Object[] info) { + String className = info[0].getClass().getSimpleName(); + String methodName = (String) info[1]; + StringBuilder output = new StringBuilder(); + + output.append("You might want to add a method to handle " + className + " events syntax is\n"); + output.append("public void " + methodName + "("); + Class[] param_classes = (Class[])(info[2]); + String[] param_names = (String[])(info[3]); + if(param_classes != null) { + for(int i = 0; i < param_classes.length; i++){ + String pname = (param_classes[i]).getSimpleName(); + output.append(pname + " " + param_names[i]); + if(i < param_classes.length - 1) + output.append(", "); + } + } + + output.append(") { /* code */ }\n"); + System.out.println(output.toString()); + } + + /** + * Unable to find the user defined handler method. + * + * info[0] event generator class + * info[1] event handling method name + * info[2] the parameter class names + * + * + * @param obj1 the object generating the method + * @param obj2 the method name + * @param obj3 parameter types (Class[]) + */ + private static void nonexistantEventHandler(Object[] info) { + String className = info[0].getClass().getSimpleName(); + String methodName = (String) info[1]; + String pname; + StringBuilder output = new StringBuilder(); + + output.append("The "+className+" class cannot find this method \n"); + output.append("\tpublic void " + methodName + "("); + Class[] param_names = (Class[])(info[2]); + for(int i = 0; i < param_names.length; i++){ + pname = (param_names[i]).getSimpleName(); + output.append(pname + " " + pname.substring(1).toLowerCase()); + if(i < param_names.length - 1) + output.append(", "); + } + output.append(") { /* code */ }\n"); + System.out.println(output.toString()); + } + + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GOption.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GOption.java new file mode 100644 index 0000000..54efe9d --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GOption.java @@ -0,0 +1,80 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import processing.core.PApplet; + +/** + * A two-state toggle control.
+ * + * GOption objects (also known as radio buttons) are two-state toggle switches that can either + * be used independently or if added to a GToggleGroup control part of a single selection + * option group. + * + * @author Peter Lager + * + */ +public class GOption extends GToggleControl{ + + /** + * Create an option button without text. + * + * @param theApplet that will display the control + * @param p0 + * @param p1 + * @param p2 + * @param p3 + */ + public GOption(PApplet theApplet, float p0, float p1, float p2, float p3) { + this(theApplet, p0, p1, p2, p3, ""); + } + + /** + * Create an option button with text. + * + * @param theApplet that will display the control + * @param p0 + * @param p1 + * @param p2 + * @param p3 + * @param text text to be displayed + */ + public GOption(PApplet theApplet, float p0, float p1, float p2, float p3, String text) { + super(theApplet, p0, p1, p2, p3); + opaque = false; + setText(text); + setIcon("pinhead.png", 2, GAlign.LEFT, null); + setTextAlign(GAlign.LEFT, null); + z = Z_SLIPPY; + // Now register control with applet + createEventHandler(G4P.sketchApplet, "handleToggleControlEvents", + new Class[]{ GToggleControl.class, GEvent.class }, + new String[]{ "option", "event" } + ); + registeredMethods = DRAW_METHOD | MOUSE_METHOD; + cursorOver = HAND; + G4P.addControl(this); + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GPanel.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GPanel.java new file mode 100644 index 0000000..64fc3d1 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GPanel.java @@ -0,0 +1,535 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSrect; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.font.TextLayout; +import java.util.LinkedList; + +import processing.core.PApplet; +import processing.core.PGraphicsJava2D; +import processing.event.MouseEvent; + + +/** + * A component that can be used to group GUI components that can be + * dragged, collapsed (leaves title tab only) and un-collapsed. + * + * When created the Panel is collapsed by default. To open the panel + * use setCollapsed(true); after creating it.
+ * + * Once a component has been added the x/y coordinates of the control are + * calculated to be the centre of the panel to the centre of the control. This + * is to facilitate rotating of controls on panels + * + * @author Peter Lager + * + */ +public class GPanel extends GTextBase { + + static protected int COLLAPSED_BAR_SPOT = 1; + static protected int EXPANDED_BAR_SPOT = 2; + static protected int SURFACE_SPOT = 0; + + + /** Whether the panel is displayed in full or tab only */ + protected boolean tabOnly = false; + + /** The height of the tab calculated from font height + padding */ + protected int tabHeight, tabWidth; + + /** Used to restore position when closing panel */ + protected float dockX, dockY; + + // Defines the area that the panel must fit inside. + protected float lowX, highX, lowY, highY; + + /** true if the panel is being dragged */ + protected boolean beingDragged = false; + + protected boolean draggable = true; + protected boolean collapsible = true; + + + /** + * Create a Panel that comprises of 2 parts the tab which is used to + * select and move the panel and the container window below the tab which + * is used to hold other components.
+ * If the panel fits inside the display window then its position will be + * constrained so that it can't be dragged outside the viewable area. + * Otherwise no constraint is applied. + * + * @param theApplet the PApplet reference + * @param p0 horizontal position + * @param p1 vertical position + * @param p2 width of the panel + * @param p3 height of the panel (excl. tab) + */ + public GPanel(PApplet theApplet, float p0, float p1, float p2, float p3) { + this(theApplet, p0, p1, 2, p3, "Panel"); + } + + /** + * Create a Panel that comprises of 2 parts the tab which is used to + * select and move the panel and the container window below the tab which + * is used to hold other components.
+ * If the panel fits inside the display window then its position will be + * constrained so that it can't be dragged outside the viewable area. + * Otherwise no constraint is applied. + * + * @param theApplet the PApplet reference + * @param p0 horizontal position + * @param p1 vertical position + * @param p2 width of the panel + * @param p3 height of the panel (excl. tab) + * @param text to appear on tab + */ + public GPanel(PApplet theApplet, float p0, float p1, float p2, float p3, String text) { + super(theApplet, p0, p1, p2, p3); + // Set the values used to constrain movement of the panel + if(x < 0 || y < 0 || x + width > winApp.width || y+ height > winApp.height) + clearDragArea(); + else + setDragArea(); + // Create the list of children + children = new LinkedList(); + // The image buffer is just for the tab area + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.rectMode(PApplet.CORNER); + buffer.g2.setFont(localFont); + setText(text); + calcHotSpots(); + constrainPanelPosition(); + opaque = true; + dockX = x; + dockY = y; + z = Z_PANEL; + + createEventHandler(G4P.sketchApplet, "handlePanelEvents", + new Class[]{ GPanel.class, GEvent.class }, + new String[]{ "panel", "event" } + ); + registeredMethods = DRAW_METHOD | MOUSE_METHOD; + cursorOver = HAND; + G4P.addControl(this); + } + + /** + * This needs to be called if the tab text is changed + */ + private void calcHotSpots(){ + hotspots = new HotSpot[]{ + new HSrect(COLLAPSED_BAR_SPOT, 0, 0, tabWidth, tabHeight), // tab text area + new HSrect(EXPANDED_BAR_SPOT, 0, 0, width, tabHeight), // tab non-text area + new HSrect(SURFACE_SPOT, 0, tabHeight, width, height - tabHeight) // panel content surface + }; + } + + /** + * This panel is being added to another additional changes that need to be made this control + * is added to another.
+ * + * In this case we need to set the constraint limits to keep inside the parent. + * + * @param p the parent + */ + protected void addToParent(GAbstractControl p){ + // Will this fit inside the parent panel + if(width > p.width || height > p.height){ //No + draggable = false; + } + else { + lowX = -p.width/2; + highX = p.width/2; + lowY = -p.height/2; + highY = p.height/2; + } + } + + public void setText(String text){ + super.setText(text); + stext.getLines(buffer.g2); + tabHeight = (int) (stext.getMaxLineHeight() + 4); + tabWidth = (int) (stext.getMaxLineLength() + 8); + calcHotSpots(); + bufferInvalid = true; + } + + public void setFont(Font font) { + if(font != null) + localFont = font; + tabHeight = (int) (1.2f * localFont.getSize() + 2); + buffer.g2.setFont(localFont); + bufferInvalid = true; + calcHotSpots(); + bufferInvalid = true; + } + + /** + * What to do when the FPanel loses focus. + */ + protected void loseFocus(GAbstractControl grabber){ + focusIsWith = null; + beingDragged = false; + } + + /** + * Draw the panel. + * If tabOnly == true + * then display the tab only + * else + * draw tab and all child (added) components + */ + public void draw(){ + if(!visible) return; + // Update buffer if invalid + updateBuffer(); + winApp.pushStyle(); + + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + // If opaque draw the panel tab and back + if(opaque){ + winApp.pushMatrix(); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + + winApp.popMatrix(); + } + // Draw the children + if(!tabOnly){ + if(children != null){ + for(GAbstractControl c : children) + c.draw(); + } + } + winApp.popMatrix(); + winApp.popStyle(); + } + + protected void updateBuffer(){ + if(bufferInvalid) { + Graphics2D g2d = buffer.g2; + buffer.beginDraw(); + + buffer.background(buffer.color(255,0)); + buffer.noStroke(); + buffer.fill(palette[4]); + if(tabOnly){ + buffer.rect(0, 0, tabWidth, tabHeight); + } + else { + buffer.rect(0, 0, width, tabHeight); + } + stext.getLines(g2d); + g2d.setColor(jpalette[12]); + TextLayout tl = stext.getTLIforLineNo(0).layout; + tl.draw(g2d, 4, 2 + tl.getAscent()); + + if(!tabOnly){ + buffer.noStroke(); + buffer.fill(palette[5]); + buffer.rect(0, tabHeight, width, height - tabHeight); + } + buffer.endDraw(); + } + } + + /** + * Determines if a particular pixel position is over the panel taking + * into account whether it is collapsed or not. + */ + public boolean isOver(float x, float y){ + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + return (tabOnly)? currSpot == COLLAPSED_BAR_SPOT : currSpot == EXPANDED_BAR_SPOT | currSpot == COLLAPSED_BAR_SPOT; + } + + /** + * All GUI components are registered for mouseEvents + */ + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + + currSpot = whichHotSpot(ox, oy); + // Is mouse over the panel tab (taking into account extended with when not collapsed) + boolean mouseOver = (tabOnly)? currSpot == COLLAPSED_BAR_SPOT : currSpot == EXPANDED_BAR_SPOT | currSpot == COLLAPSED_BAR_SPOT; + + if(mouseOver || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(focusIsWith != this && mouseOver && z >= focusObjectZ()){ + takeFocus(); + beingDragged = false; + } + break; + case MouseEvent.CLICK: + if(focusIsWith == this && collapsible){ + tabOnly = !tabOnly; + // Perform appropriate action depending on collapse state + setCollapsed(tabOnly); + if(tabOnly){ + x = dockX; + y = dockY; + } + else { + dockX = x; + dockY = y; + // Open panel move on screen if needed + if(y + height > winApp.getHeight()) + y = winApp.getHeight() - height; + if(x + width > winApp.getWidth()) + x = winApp.getWidth() - width; + } + // Maintain centre for drawing purposes + cx = x + width/2; + cy = y + height/2; + constrainPanelPosition(); + if(tabOnly) + fireEvent(this, GEvent.COLLAPSED); + else + fireEvent(this, GEvent.EXPANDED); + beingDragged = false; + // This component does not keep the focus when clicked + loseFocus(null); + } + break; + case MouseEvent.RELEASE: // After dragging NOT clicking + if(focusIsWith == this){ + if(beingDragged){ + // Remember the dock position when the mouse has + // been released after the panel has been dragged + dockX = x; + dockY = y; + beingDragged = false; + loseFocus(null); + } + } + break; + case MouseEvent.DRAG: + if(focusIsWith == this && draggable ){//&& parent == null){ + // Maintain centre for drawing purposes + cx += (winApp.mouseX - winApp.pmouseX); + cy += (winApp.mouseY - winApp.pmouseY); + // Update x and y positions + x = cx - width/2; + y = cy - height/2; + constrainPanelPosition(); + beingDragged = true; + fireEvent(this, GEvent.DRAGGED); + } + break; + } + } + + /** + * Determines whether to show the tab and panel back colour. If the + * parameter is the same as the current state then no changes will + * be made.
+ * If the parameter is false then the panel will be
+ *
    + *
  • expanded
  • + *
  • made non-collasible
  • + *
  • made unavailable to mouse control (so can't be dragged)
  • + *
+ * If the parameter is true then the panel will remain non-collapsible + * and the user must change this if required.
+ * @param opaque + */ + public void setOpaque(boolean opaque){ + if(this.opaque == opaque) + return; // no change + if(!opaque){ + setCollapsed(false); + setCollapsible(false); + } + available = opaque; + this.opaque = opaque; + } + + /** + * This method is used to discover whether the panel is being + * dragged to a new position on the screen. + * @return true if being dragged to a new position + */ + public boolean isDragging(){ + return beingDragged; + } + + /** + * Sets whether the panel can be dragged by the mouse or not. + * @param draggable + */ + public void setDraggable(boolean draggable){ + this.draggable = draggable; + } + + /** + * Can we drag this panel with the mouse? + * @return true if draggable + */ + public boolean isDraggable(){ + return draggable; + } + + /** + * Collapse or open the panel + * @param collapse + */ + public void setCollapsed(boolean collapse){ + if(collapsible){ + tabOnly = collapse; + // If we open the panel make sure it fits on the screen but if we collapse + // the panel disable the panel controls but leave the panel available + if(tabOnly){ + setAvailable(false); + available = true; // Needed so we can click on the title bar + } + else { + setAvailable(true); + } + } + } + + /** + * Find out if the panel is collapsed + * @return true if collapsed + */ + public boolean isCollapsed(){ + return tabOnly; + } + + /** + * Determine whether the panel can be collapsed when the title bar is clicked.
+ * + * If this is set to false then the panel will be expanded and it will + * not be possible to collapse it until set back to true. + * + */ + public void setCollapsible(boolean c){ + collapsible = c; + if(c == false){ + tabOnly = false; + setAvailable(true); + } + } + + /** + * Is this panel collapsible + */ + public boolean isCollapsible(){ + return collapsible; + } + + public int getTabHeight(){ + return tabHeight; + } + + /** + * Provided the panel is physically small enough this method will set the area + * within which the panel can be dragged and move the panel inside the area if + * not already inside.
+ * + * @param xMin + * @param yMin + * @param xMax + * @param yMax + * @return true if the constraint was applied successfully else false + */ + public boolean setDragArea(float xMin, float yMin, float xMax, float yMax){ + if(xMax - xMin < width || yMax - yMin < height){ + if(G4P.showMessages) + System.out.println("The constraint area is too small for this panel - request ignored"); + return false; + } + lowX = xMin; + lowY = yMin; + highX = xMax; + highY = yMax; + constrainPanelPosition(); + return true; + } + + /** + * Provided the panel is small enough to fit inside the display area then + * the panel will be constrained to fit inside the display area. + * + * @return true if the constraint was applied successfully else false + */ + public boolean setDragArea(){ + return setDragArea(0, 0, winApp.width, winApp.height); + } + + /** + * Remove any drag constraint from this panel. + */ + public void clearDragArea(){ + lowX = lowY = -Float.MAX_VALUE; + highX = highY = Float.MAX_VALUE; + } + + /** + * Ensures that the panel tab and panel body if open does not + * extend off the screen. + */ + private void constrainPanelPosition(){ + // Calculate the size of the visible part of the panel + int w = (int) ((tabOnly)? tabWidth : width); + int h = (int) ((tabOnly)? tabHeight : height); + // Constrain horizontally + if(x < lowX) + x = lowX; + else if(x + w > highX) + x = (int) (highX - w); + // Constrain vertically + if(y < lowY) + y = lowY; + else if(y + h > highY) + y = highY - h; + // Maintain centre for + cx = x + width/2; + cy = y + height/2; + } + + + public String toString(){ + return tag + " [" + x + ", " + y+"]" + " [" + cx + ", " + cy+"]"+ " [" + dockX + ", " + dockY+"]"; + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GScrollbar.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GScrollbar.java new file mode 100644 index 0000000..d22f0f9 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GScrollbar.java @@ -0,0 +1,318 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSrect; + +import java.awt.Graphics2D; +import java.awt.geom.RoundRectangle2D; + +import processing.core.PApplet; +import processing.core.PGraphicsJava2D; +import processing.event.MouseEvent; + +/** + * This class is only used by the GDropList, GTextField and GTextArea components to provide + * a scrollbar. + * + * @author Peter Lager + * + */ +class GScrollbar extends GAbstractControl { + + private static final int OFF_FILL = 3; + private static final int OFF_STROKE = 0; + private static final int OVER_FILL = 1; + private static final int OVER_STROKE = 3; + private static final int TRACK = 5; + + protected RoundRectangle2D lowCap, highCap; + + protected float value = 0.2f; + protected float filler = .5f; + protected boolean autoHide = true; + protected boolean currOverThumb = false; + protected boolean isValueChanging = false; + + protected float last_ox, last_oy; + + /** + * Create the scroll bar + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + */ + public GScrollbar(PApplet theApplet, float p0, float p1, float p2, float p3) { + super(theApplet, p0, p1, p2, p3); + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.rectMode(PApplet.CORNER); + hotspots = new HotSpot[]{ + new HSrect(1, 0, 0, 16, height), // low cap + new HSrect(2, width - 16, 0, 16, height), // high cap + new HSrect(9, 17, 0, width - 17, height) // thumb track + }; + + lowCap = new RoundRectangle2D.Float(1, 1, 15, height-2, 6, 6); + highCap = new RoundRectangle2D.Float(width - 15, 1, 14.5f, height-2, 6, 6); + + opaque = false; + + z = Z_SLIPPY; + registeredMethods = DRAW_METHOD | MOUSE_METHOD; + cursorOver = HAND; + G4P.addControl(this); + } + + /** + * If set to true then the scroll bar is only displayed when needed. + * + * @param autoHide + */ + public void setAutoHide(boolean autoHide){ + if(this.autoHide != autoHide){ + this.autoHide = autoHide; + if(this.autoHide && filler > 0.99999f) + visible = false; + bufferInvalid = true; + } + } + + /** + * Set the position of the thumb. If the value forces the thumb + * past the end of the scrollbar, reduce the filler. + * + * @param value must be in the range 0.0 to 1.0 + */ + public void setValue(float value){ + if(value + filler > 1) + filler = 1 - value; + this.value = value; + if(autoHide && filler > 0.99999f) + visible = false; + else + visible = true; + bufferInvalid = true; + } + + /** + * Set the value and the thumb size. Force the value to be valid + * depending on filler. + * @param value must be in the range 0.0 to 1.0 + * @param filler must be >0 and <= 1 + */ + public void setValue(float value, float filler){ + if(value + filler > 1) + value = 1 - filler; + this.value = value; + this.filler = filler; + if(autoHide && this.filler > 0.99999f) + visible = false; + else + visible = true; + bufferInvalid = true; + } + + /** + * Get the current value of the scrolbar + * @return + */ + public float getValue(){ + return value; + } + + /** + * All GUI components are registered for mouseEvents + */ + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + + int spot = whichHotSpot(ox, oy); + + // If over the track then see if we are over the thumb + if(spot >= 9){ + if(isOverThumb(ox, oy)) + spot = 10; + else + spot = -1; // Over empty track so ignore + } + if(spot != currSpot){ + currSpot = spot; + bufferInvalid = true; + } + + if(currSpot >= 0 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(focusIsWith != this && currSpot>= 0 && z > focusObjectZ()){ + dragging = false; + last_ox = ox; last_oy = oy; + takeFocus(); + } + break; + case MouseEvent.CLICK: + if(focusIsWith == this){ + switch(currSpot){ + case 1: + value -= 0.1f; + if(value < 0) + value = 0; + bufferInvalid = true; + fireEvent(this, GEvent.CHANGED); + break; + case 2: + value += 0.1f; + if(value + filler > 1.0) + value = 1 - filler; + bufferInvalid = true; + fireEvent(this, GEvent.CHANGED); + break; + } + dragging = false; + loseFocus(parent); + } + break; + case MouseEvent.RELEASE: + if(focusIsWith == this && dragging){ + loseFocus(parent); + dragging = false; + isValueChanging = false; + bufferInvalid = true; + } + break; + case MouseEvent.DRAG: + if(focusIsWith == this){ + float movement = ox - last_ox; + last_ox = ox; + float deltaV = movement / (width - 32); + value += deltaV; + value = PApplet.constrain(value, 0, 1.0f - filler); + isValueChanging = true; + bufferInvalid = true; + dragging = true; + fireEvent(this, GEvent.CHANGED); + } + break; + } + } + + protected boolean isOverThumb(float px, float py){ + float p = (px - 16) / (width - 32); + boolean over =( p >= value && p < value + filler); + return over; + } + + protected void updateBuffer(){ + if(bufferInvalid) { + bufferInvalid = false; + Graphics2D g2d = buffer.g2; + buffer.beginDraw(); + if(opaque) { + buffer.background(buffer.color(255,0)); + buffer.fill(palette[6]); + buffer.noStroke(); + buffer.rect(8,0,width-16,height); + } + else + buffer.background(buffer.color(255,0)); + // Draw the track + buffer.fill(palette[TRACK]); + buffer.noStroke(); + buffer.rect(8,3,width-8,height-5); + g2d.setStroke(pen_1_0); + + // Draw the low cap + buffer.strokeWeight(1.2f); + if(currSpot == 1){ + g2d.setColor(jpalette[OVER_FILL]); + g2d.fill(lowCap); + g2d.setColor(jpalette[OVER_STROKE]); + g2d.draw(lowCap); + } + else { + g2d.setColor(jpalette[OFF_FILL]); + g2d.fill(lowCap); + g2d.setColor(jpalette[OFF_STROKE]); + g2d.draw(lowCap); + } + // Draw the high cap + if(currSpot == 2){ + g2d.setColor(jpalette[OVER_FILL]); + g2d.fill(highCap); + g2d.setColor(jpalette[OVER_STROKE]); + g2d.draw(highCap); + } + else { + g2d.setColor(jpalette[OFF_FILL]); + g2d.fill(highCap); + g2d.setColor(jpalette[OFF_STROKE]); + g2d.draw(highCap); + } + // draw thumb + float thumbWidth = (width - 32) * filler; + RoundRectangle2D thumb = new RoundRectangle2D.Float(1,1,thumbWidth-1, height-2,6,6); + buffer.translate((width - 32) * value + 16, 0); + if(currSpot == 10 || isValueChanging){ + g2d.setColor(jpalette[OVER_FILL]); + g2d.fill(thumb); + g2d.setColor(jpalette[OVER_STROKE]); + g2d.draw(thumb); + } + else { + g2d.setColor(jpalette[OFF_FILL]); + g2d.fill(thumb); + g2d.setColor(jpalette[OFF_STROKE]); + g2d.draw(thumb); + } + buffer.endDraw(); + } + } + + public void draw(){ + if(!visible) return; + if(bufferInvalid) + updateBuffer(); + + winApp.pushStyle(); + winApp.pushMatrix(); + + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + winApp.imageMode(PApplet.CENTER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + + winApp.popMatrix(); + winApp.popStyle(); + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GSketchPad.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GSketchPad.java new file mode 100644 index 0000000..715e817 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GSketchPad.java @@ -0,0 +1,93 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2012 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import processing.core.PApplet; +import processing.core.PGraphics; + +/** + * Display area for user generated graphics.
+ * + * This control will display a PGraphics object created and updated by the user. + * If the size of the users graphic is different from the control the output will be + * rescaled to fit the control size irrespective of the aspect ratio. + * + * @author Peter Lager + * + */ +public class GSketchPad extends GAbstractControl { + + // Scale graphic should be set to true if the grpahics object + // and this sketch pad object are of different sizes. + protected boolean scaleGraphic = false; + + protected PGraphics pad = null; + + public GSketchPad(PApplet theApplet, float p0, float p1, float p2, float p3) { + super(theApplet, p0, p1, p2, p3); + cursorOver = G4P.mouseOff; // does not change + registeredMethods = DRAW_METHOD; + G4P.addControl(this); + } + + public void setGraphic(PGraphics pg){ + if(pg == null) + return; + pad = pg; + scaleGraphic = (int)width != pg.width || (int)height != pg.height; + } + + public void draw(){ + if(!visible) return; + + winApp.pushStyle(); + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + if(pad != null){ + try { + if(scaleGraphic) + winApp.image(pad, 0, 0, width, height); + else + winApp.image(pad, 0, 0); +// System.out.println("Graphic updated with alpha " + alphaLevel); + } + catch(Exception excp){ /* Do nothing */ } + } +// winApp.noFill(); +// winApp.stroke(palette[3]); +// winApp.strokeWeight(1.5f); +// winApp.rect(0, 0, width, height); + winApp.popMatrix(); + winApp.popStyle(); + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GSlider.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GSlider.java new file mode 100644 index 0000000..87a2d98 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GSlider.java @@ -0,0 +1,181 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-13 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HScircle; +import g4p_controls.HotSpot.HSrect; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.RoundRectangle2D; + +import processing.core.PApplet; +import processing.core.PGraphicsJava2D; + +/** + * A simple graphical slider. + * + * Either drag the thumb or click on the track to change the slider value.
+ * + * Supports
+ * user defined limits (ascending or descending values)
+ * numeric display for limits and current value
+ * track ticks and stick to ticks
+ * + * + * @author Peter Lager + * + */ +public class GSlider extends GLinearTrackControl { + + protected RoundRectangle2D track; + + public GSlider(PApplet theApplet, float p0, float p1, float p2, float p3, float tr_width) { + super(theApplet, p0, p1, p2, p3); + trackWidth = tr_width; + trackDisplayLength = width - 2 * TINSET; + trackLength = trackDisplayLength - trackWidth; + track = new RoundRectangle2D.Float(-trackDisplayLength/2, -trackWidth/2, + trackDisplayLength, trackWidth, + trackWidth, trackWidth ); + trackOffset = calcTrackOffset(); + + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + buffer.g2.setFont(G4P.numericLabelFont); + hotspots = new HotSpot[]{ + new HScircle(THUMB_SPOT, width/2 + (parametricPos - 0.5f) * trackLength, height/2, trackWidth/2 ), // thumb + new HSrect(TRACK_SPOT, (width-trackLength)/2, (height-trackWidth)/2, trackLength, trackWidth), // track + }; + z = Z_SLIPPY; + + epsilon = 0.98f / trackLength; + + ssStartLimit = new StyledString("0.00"); + ssEndLimit = new StyledString("1.00"); + ssValue = new StyledString("0.50"); + + // Now register control with applet + createEventHandler(G4P.sketchApplet, "handleSliderEvents", + new Class[]{ GValueControl.class, GEvent.class }, + new String[]{ "slider", "event" } + ); + registeredMethods = PRE_METHOD | DRAW_METHOD | MOUSE_METHOD; + cursorOver = HAND; + G4P.addControl(this); + } + + protected void updateDueToValueChanging(){ + hotspots[0].x = (width/2 + (parametricPos - 0.5f) * trackLength); + } + + /** + * Enable or disable the ability of the component to generate mouse events.
+ * GTextField - it also controls key press events
+ * GPanel - controls whether the panel can be moved/collapsed/expanded
+ * @param enable true to enable else false + */ + public void setEnabled(boolean enable){ + super.setEnabled(enable); + if(!enable) + status = OFF_CONTROL; + } + + // Palette index constants + static int DBORDER = 1, LBORDER = 3, BACK = 6; + static int TBORDER = 15, TOFF = 3, TOVER = 11, TDOWN = 14, TDRAG = 15; + + protected void updateBuffer(){ + if(bufferInvalid) { + Graphics2D g2d = buffer.g2; + bufferInvalid = false; + buffer.beginDraw(); + buffer.rectMode(PApplet.CENTER); + buffer.ellipseMode(PApplet.CENTER); + // Back ground colour + if(opaque == true) + buffer.background(palette[BACK]); + else + buffer.background(buffer.color(255,0)); + + // Draw track, thumb, ticks etc. + buffer.pushMatrix(); + buffer.translate(width/2, height/2); + // draw ticks + if(showTicks){ + float delta = 1.0f / (nbrTicks - 1); + for(int i = 0; i < nbrTicks; i++){ + float tickx = ((i * delta - 0.5f)*trackLength); + buffer.strokeWeight(2); + buffer.stroke(palette[4]); + buffer.line(tickx, -trackWidth, tickx, trackWidth); + buffer.strokeWeight(1.2f); + buffer.stroke(palette[1]); + buffer.line(tickx, -trackWidth, tickx, trackWidth); + } + } + // Draw track surface + g2d.setColor(jpalette[5]); + g2d.fill(track); + // Draw thumb + switch(status){ + case OFF_CONTROL: + buffer.fill(palette[TOFF]); + break; + case OVER_CONTROL: + buffer.fill(palette[TOVER]); + break; + case PRESS_CONTROL: + buffer.fill(palette[TDOWN]); + break; + case DRAG_CONTROL: + buffer.fill(palette[TDRAG]); + break; + } + buffer.noStroke(); + buffer.ellipse((parametricPos - 0.5f) * trackLength, 0, trackWidth, trackWidth); + // Draw track border + g2d.setStroke(pen_2_0); + g2d.setColor(jpalette[3]); + g2d.draw(track); + + // Display slider values + g2d.setColor(jpalette[2]); + if(labels != null){ + drawLabels(); + } + else { + if(showLimits) + drawLimits(); + // Display slider value + if(showValue) + drawValue(); + } + buffer.popMatrix(); + buffer.endDraw(); + } + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GSlider2D.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GSlider2D.java new file mode 100644 index 0000000..2bc09f9 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GSlider2D.java @@ -0,0 +1,615 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2013 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSrect; + +import java.awt.RenderingHints; + +import processing.core.PApplet; +import processing.core.PGraphicsJava2D; +import processing.event.MouseEvent; + +/** + * This slider is used to control 2 variables by dragging the thumb over + * a 2D surface. It has all the features of the standard slider (GSlider) + * except that it does not have ticks or stick-to-ticks functionality.
+ * + * If no limits are set then the control will return a value in the range + * 0.0 to 1.0 for both the x and the y axis. The setXlimits and setYlimits + * can be used to set a different range for each axis independently. + * + * The minimum size for this control is 40x40 pixels and this is enforced + * when the control is created. If necessary the width and/or height the + * rectangle will be increased to 40pixels.
+ * + * @author Peter Lager + * + */ +public class GSlider2D extends GValueControl2D { + + // Palette index constants + static int DBORDER = 1, LBORDER = 3, BACK = 6; + static int TBORDER = 15, TOFF = 3, TOVER = 11, TDOWN = 14, TDRAG = 15; + + static final float THUMB_SIZE = 10; + static final float HALF_THUMB_SIZE = THUMB_SIZE / 2; + static final float BORDER_WIDTH = 2; + + // Define the drag area for this control + protected float dragWidth, dragHeight, dragD; + + protected int downHotSpot = -1; + // Mouse over status + protected int status = OFF_CONTROL; + + protected float startXlimit = 0, endXlimit = 1; + protected float startYlimit = 0, endYlimit = 1; + + /** + * Create a 2D slider inside the specified rectangle. + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + */ + public GSlider2D(PApplet theApplet, float p0, float p1, float p2, float p3) { + super(theApplet, p0, p1, p2, p3); + // Enforce minimum size constraint + if(width < 40 || height < 40) + resize(PApplet.max(width,40), PApplet.max(height,40)); + + dragWidth = width - THUMB_SIZE - 2 * BORDER_WIDTH; + dragHeight = height - THUMB_SIZE - 2 * BORDER_WIDTH; + dragD = 2 + THUMB_SIZE/2; + + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + buffer.g2.setFont(G4P.numericLabelFont); + hotspots = new HotSpot[]{ + new HSrect(THUMB_SPOT, dragD - HALF_THUMB_SIZE + parametricPosX * dragWidth, + dragD - HALF_THUMB_SIZE + parametricPosY * dragHeight, THUMB_SIZE, THUMB_SIZE ), // thumb + new HSrect(TRACK_SPOT, dragD, dragD, dragWidth, dragHeight) // track + }; + z = Z_SLIPPY; + + epsilon = 0.98f / PApplet.max(dragWidth, dragHeight); + opaque = true; + + // Now register control with applet + createEventHandler(G4P.sketchApplet, "handleSlider2DEvents", + new Class[]{ GSlider2D.class, GEvent.class }, + new String[]{ "slider2d", "event" } + ); + registeredMethods = PRE_METHOD | DRAW_METHOD | MOUSE_METHOD; + cursorOver = HAND; + G4P.addControl(this); + } + + /** + * Updates thumb hotspot due changes caused by easing + */ + protected void updateDueToValueChanging(){ + hotspots[0].x = dragD - HALF_THUMB_SIZE + parametricPosX * dragWidth; + hotspots[0].y = dragD - HALF_THUMB_SIZE + parametricPosY * dragHeight; + } + + + /** + * Set the amount of easing to be used when a value is changing. The default value + * is 1 (no easing) values > 1 will cause the value to rush from its starting value + * and decelerate towards its final values. In other words it smoothes the movement + * of the slider thumb or knob rotation. + * + * @param easeBy the easing to set + */ + public void setEasing(float easeBy) { + easing = (easeBy < 1) ? 1 : easeBy; + } + + /** + * X (horz) limits + * Sets the range of values to be returned. This method will + * assume that you want to set the valueType to INTEGER + * + * @param start the start value of the range + * @param end the end value of the range + */ + public void setXlimits(int start, int end){ + startXlimit = start; + endXlimit = end; + setEpsilon(); + valueType = INTEGER; + bufferInvalid = true; + } + + /** + * X (horz) limits + * Sets the initial value and the range of values to be returned. This + * method will assume that you want to set the valueType to INTEGER. + * + * @param initValue the initial value + * @param start the start value of the range + * @param end the end value of the range + */ + public void setLimitsX(int initValue, int start, int end){ + startXlimit = start; + endXlimit = end; + valueType = INTEGER; + setEpsilon(); + bufferInvalid = true; + setValueX(initValue); + updateDueToValueChanging(); + } + + /** + * X (horz) limits + * Sets the range of values to be returned. This method will + * assume that you want to set the valueType to DECIMAL + * + * @param start + * @param end + */ + public void setLimitsX(float start, float end){ + startXlimit = start; + endXlimit = end; + if(valueType == INTEGER){ + valueType = DECIMAL; + setPrecision(1); + } + setEpsilon(); + bufferInvalid = true; + } + + /** + * X (horz) limits + * Sets the initial value and the range of values to be returned. This + * method will assume that you want to set the valueType to DECIMAL. + * + * @param initValue the initial value + * @param start the start value of the range + * @param end the end value of the range + */ + public void setLimitsX(float initValue, float start, float end){ + startXlimit = start; + endXlimit = end; + initValue = PApplet.constrain(initValue, start, end); + if(valueType == INTEGER){ + valueType = DECIMAL; + setPrecision(1); + } + setEpsilon(); + bufferInvalid = true; + setValueX(initValue); + updateDueToValueChanging(); + } + + /** + * Y (vert) limits + * Sets the range of values to be returned. This method will + * assume that you want to set the valueType to INTEGER + * + * @param start the start value of the range + * @param end the end value of the range + */ + public void setLimitsY(int start, int end){ + startYlimit = start; + endYlimit = end; + setEpsilon(); + valueType = INTEGER; + bufferInvalid = true; + } + + /** + * Y (vert) limits + * Sets the initial value and the range of values to be returned. This + * method will assume that you want to set the valueType to INTEGER. + * + * @param initValue the initial value + * @param start the start value of the range + * @param end the end value of the range + */ + public void setLimitsY(int initValue, int start, int end){ + startYlimit = start; + endYlimit = end; + valueType = INTEGER; + setEpsilon(); + bufferInvalid = true; + setValueY(initValue); + updateDueToValueChanging(); + } + + /** + * Y (vert) limits + * Sets the range of values to be returned. This method will + * assume that you want to set the valueType to DECIMAL + * + * @param start + * @param end + */ + public void setLimitsY(float start, float end){ + startYlimit = start; + endYlimit = end; + if(valueType == INTEGER){ + valueType = DECIMAL; + setPrecision(1); + } + setEpsilon(); + bufferInvalid = true; + } + + /** + * Y (vert) limits + * Sets the initial value and the range of values to be returned. This + * method will assume that you want to set the valueType to DECIMAL. + * + * @param initValue the initial value + * @param start the start value of the range + * @param end the end value of the range + */ + public void setLimitsY(float initValue, float start, float end){ + startYlimit = start; + endYlimit = end; + initValue = PApplet.constrain(initValue, start, end); + if(valueType == INTEGER){ + valueType = DECIMAL; + setPrecision(1); + } + setEpsilon(); + bufferInvalid = true; + setValueY(initValue); + updateDueToValueChanging(); + } + + /** + * Set the X (horz) value for the slider.
+ * The value supplied will be constrained to the current limits. + * @param v the new value + */ + public void setValueX(float v){ + if(valueType == INTEGER) + v = Math.round(v); + float t = (v - startXlimit) / (endXlimit - startXlimit); + t = PApplet.constrain(t, 0.0f, 1.0f); + parametricTargetX = t; + } + + /** + * Set the Y (vert) value for the slider.
+ * The value supplied will be constrained to the current limits. + * @param v the new value + */ + public void setValueY(float v){ + if(valueType == INTEGER) + v = Math.round(v); + float t = (v - startYlimit) / (endYlimit - startYlimit); + t = PApplet.constrain(t, 0.0f, 1.0f); + parametricTargetY = t; + } + + /** + * Set both the XY values for the slider.
+ * The values supplied will be constrained to the appropriate current limits. + * @param vx the new X (horz) value + * @param vy the new Y (vert) value + */ + public void setValueXY(float vx, float vy){ + setValueX(vx); + setValueY(vy); + } + + /** + * Get the current X value as a float + */ + public float getValueXF(){ + return startXlimit + (endXlimit - startXlimit) * parametricPosX; + } + + /** + * Get the current X value as an integer.
+ * DECIMAL and EXPONENT value types will be rounded to the nearest integer. + */ + public int getValueXI(){ + return Math.round(getValueXF()); + } + + /** + * Get the current X value as a string taking into account the number format.
+ */ + public String getValueXS(){ + return getNumericDisplayString(getValueXF()); + } + + /** + * Get the current Y value as a float + */ + public float getValueYF(){ + return startYlimit + (endYlimit - startYlimit) * parametricPosY; + } + + /** + * Get the current Y value as an integer.
+ * DECIMAL and EXPONENT value types will be rounded to the nearest integer. + */ + public int getValueYI(){ + return Math.round(getValueYF()); + } + + /** + * Get the current Y value as a string taking into account the number format.
+ */ + public String getValueYS(){ + return getNumericDisplayString(getValueYF()); + } + + /** + * Used to format the number into a string for display. + * @param number + * @return the number formated as a string + */ + protected String getNumericDisplayString(float number){ + String s = ""; + switch(valueType){ + case INTEGER: + s = String.format("%d", Math.round(number)); + break; + case DECIMAL: + s = String.format("%." + precision + "f", number); + break; + case EXPONENT: + s = String.format("%." + precision + "e", number); + break; + } + return s.trim(); + } + + /** + * For DECIMAL values this sets the number of decimal places to + * be displayed. + * @param nd must be >= 1 otherwise will use 1 + */ + public void setPrecision(int nd){ + nd = PApplet.constrain(nd, 1, 5); + if(nd < 1) + nd = 1; + if(nd != precision){ + precision = nd; + setEpsilon(); + bufferInvalid = true; + } + } + + /** + * Set the numberFormat and precision in one go.
+ * Valid number formats are INTEGER, DECIMAL, EXPONENT
+ * Precision must be >= 1 and is ignored for INTEGER. + * + * @param numberFormat G4P.INTEGER, G4P.DECIMAL orG4P. EXPONENT + * @param precision must be >= 1 + */ + public void setNumberFormat(int numberFormat, int precision){ + switch(numberFormat){ + case INTEGER: + case DECIMAL: + case EXPONENT: + this.valueType = numberFormat; + break; + default: + valueType = DECIMAL; + } + setPrecision(precision); + bufferInvalid = true; + } + + /** + * Set the numberFormat and precision in one go.
+ * Valid number formats are INTEGER, DECIMAL, EXPONENT
+ * Precision must be >= 1 and is ignored for INTEGER. + * + * @param numberFormat G4P.INTEGER, G4P.DECIMAL or G4P.EXPONENT + */ + public void setNumberFormat(int numberFormat){ + switch(numberFormat){ + case INTEGER: + case DECIMAL: + case EXPONENT: + this.valueType = numberFormat; + break; + default: + valueType = DECIMAL; + } + bufferInvalid = true; + } + + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + // Make ox,oy relative to top-left of drag area + ox -= dragD; + oy -= dragD; + + if(currSpot >= 0 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(focusIsWith != this && currSpot > -1 && z >= focusObjectZ()){ + downHotSpot = currSpot; + status = (downHotSpot == THUMB_SPOT) ? PRESS_CONTROL : OFF_CONTROL; + offsetH = ox - parametricPosX * dragWidth; // normalised + offsetV = oy - parametricPosY * dragHeight; // normalised + takeFocus(); + bufferInvalid = true; + } + break; + case MouseEvent.CLICK: + if(focusIsWith == this){ + parametricTargetX = ox / dragWidth; + parametricTargetY = oy / dragHeight; + dragging = false; + status = OFF_CONTROL; + loseFocus(null); + bufferInvalid = true; + } + break; + case MouseEvent.RELEASE: + if(focusIsWith == this && dragging){ + if(downHotSpot == THUMB_SPOT){ + mouseUpdateTargets(); + } + status = OFF_CONTROL; + bufferInvalid = true; + loseFocus(null); + } + dragging = false; + break; + case MouseEvent.DRAG: + if(focusIsWith == this){ + status = DRAG_CONTROL; + dragging = true; + if(downHotSpot == THUMB_SPOT){ + mouseUpdateTargets(); + bufferInvalid = true; + } + } + break; + case MouseEvent.MOVE: + int currStatus = status; + // If dragged state will stay as PRESSED + if(currSpot == THUMB_SPOT) + status = OVER_CONTROL; + else + status = OFF_CONTROL; + if(currStatus != status) + bufferInvalid = true; + break; + } + } + + /** + * Convenience method called during mouse event handling + */ + private void mouseUpdateTargets(){ + parametricTargetX = ox / dragWidth; + if(parametricTargetX < 0){ + parametricTargetX = 0; + offsetH = 0; + } + else if(parametricTargetX > 1){ + parametricTargetX = 1; + offsetH = 0; + } + parametricTargetY = oy / dragHeight; + if(parametricTargetY < 0){ + parametricTargetY = 0; + offsetV = 0; + } + else if(parametricTargetY > 1){ + parametricTargetY = 1; + offsetV = 0; + } + } + + public void draw(){ + if(!visible) return; + // Update buffer if invalid + updateBuffer(); + winApp.pushStyle(); + + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + winApp.pushMatrix(); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + winApp.popMatrix(); + winApp.popMatrix(); + + winApp.popStyle(); + } + + protected void updateBuffer(){ + if(bufferInvalid) { + bufferInvalid = false; + buffer.beginDraw(); + + buffer.ellipseMode(PApplet.CENTER); + buffer.rectMode(PApplet.CENTER); + // Back ground colour + if(opaque == true) + buffer.background(palette[BACK]); + else + buffer.background(buffer.color(255,0)); + + buffer.pushMatrix(); + // Draw thumb cursor lines + float tx = dragD + parametricPosX * dragWidth; + float ty = dragD + parametricPosY * dragHeight; + buffer.stroke(palette[TBORDER]); + buffer.strokeWeight(1); + buffer.line(0, ty, width, ty); + buffer.line(tx, 0, tx, height); + switch(status){ + case OFF_CONTROL: + buffer.fill(palette[TOFF]); + break; + case OVER_CONTROL: + buffer.fill(palette[TOVER]); + break; + case PRESS_CONTROL: + buffer.fill(palette[TDOWN]); + break; + case DRAG_CONTROL: + buffer.fill(palette[TDRAG]); + break; + } + buffer.rect(tx, ty, THUMB_SIZE, THUMB_SIZE); + + // Draw control border + buffer.rectMode(PApplet.CORNERS); + buffer.noFill(); + buffer.stroke(palette[LBORDER]); + buffer.strokeWeight(2); + buffer.rect(0,0,width-1,height-1); + buffer.stroke(palette[DBORDER]); + buffer.strokeWeight(1); + buffer.rect(1,1,width-1,height-1); + + buffer.popMatrix(); + buffer.endDraw(); + } + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GStick.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GStick.java new file mode 100644 index 0000000..ba27183 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GStick.java @@ -0,0 +1,442 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HScircle; + +import java.awt.RenderingHints; + +import processing.core.PApplet; +import processing.core.PGraphicsJava2D; +import processing.event.MouseEvent; + +/** + * This control simulates a digital joystick and is designed to give more + * intuitive control in game scenarios where you might use the keyboard + * e.g. WASD keys for movement.
+ * + * The joystick has two modes - in the default mode the joystick just + * responds to movement in 4 directions (left,right, up and down) the + * second mode allows for diagonals so recognises 8 directions.
+ * + * As in a real joystick you have a dead zone near the centre which + * does not generate signals, thus avoiding jitter. This area is shown + * graphically.
+ * + * The direction of the joystick is represented by an integer in the + * range 0-7 and -1 when in the dead zone.
+ *
+ *     5   6   7
+ * 		\  |  /
+ *       \ | /
+ *   4 --- + --- 0         + is the dea zone so -1
+ *       / | \
+ *      /  |  \
+ *     3   2   1
+ * 

+ * As well as the direction there are two useful methods to decode these + * into X and Y directions -
getStickX()
and
getStickY()
+ * which give the values.
+ *
+ * X= -1   0   +1 
+ * 		\  |  /  -1
+ *       \ | /
+ *     --- + ---  0
+ *       / | \
+ *      /  |  \
+ *           Y=  +1
+ * 

+ * The stick will auto-center when released.
+ * + * The minimum size for this control is 40x40 pixels and this is enforced + * when the control is created. If necessary the width and/or height the + * rectangle will be increased to 40pixels. + * + * @author Peter Lager + * + */ +public class GStick extends GAbstractControl { + // palette index constants + protected static final int BORDERS = 0; + protected static final int LED_INACTIVE = 1; + protected static final int LED_ACTIVE = 14; + protected static final int STICK = 0; + protected static final int STICK_TOP = 3; + protected static final int STICK_TOP_OVER = 11; + protected static final int STICK_TOP_PRESS = 14; + protected static final int STICK_TOP_DRAG = 15; + protected static final int OUTERRING = 6; + protected static final int ACTIONRING = 5; + protected static final int BACK = 6; + protected static final int ROD = 1; + //angle constants + protected static final float RAD90 = PApplet.radians(90); + protected static final float RAD45 = PApplet.radians(45); + protected static final float RAD22_5 = PApplet.radians(22.5f); + + + protected static final int[] posMap = new int[] { 0x01, 0x07, 0x04, 0x1c, 0x10, 0x70, 0x40, 0xc1 }; + protected static final int[] posX = new int[] { 1, 1, 0, -1, -1, -1, 0, 1 }; + protected static final int[] posY= new int[] { 0, 1, 1, 1, 0, -1, -1, -1 }; + + + protected final float ledWidth, ledHeight; + protected float ledRingRad; + + protected float actionRad, actionRadLimit, gripRad, rodRad; + protected float rodLength = 0, stickAngle; + + protected int position = -1; + protected int mode = X4; // X4 or X8 + protected int status = OFF_CONTROL; + + /** + * Create the stick inside the specified rectangle. + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + */ + public GStick(PApplet theApplet, float p0, float p1, float p2, float p3) { + super(theApplet, p0, p1, p2, p3); + // Enforce minimum size constraint + if(width < 40 || height < 40) + resize(PApplet.max(width,40), PApplet.max(height,40)); + + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + buffer.rectMode(PApplet.CORNER); + buffer.ellipseMode(PApplet.CORNER); + opaque = false; + // Calculate stick metrics + float stickSize = PApplet.min(width, height); + float mag = stickSize/50; + ledWidth = 6 * mag; + ledHeight = 1.6f * ledWidth; + ledRingRad = (stickSize - ledWidth - 3)/2; + actionRad = 0.50f * ledRingRad; + gripRad = 4.0f * mag; + rodRad = 3.0f * mag; + actionRadLimit = ledRingRad - gripRad - ledWidth/2; + + hotspots = new HotSpot[]{ + new HScircle(1, width/2, height/2, gripRad) + }; + z = Z_SLIPPY; + + // Now register control with applet + createEventHandler(G4P.sketchApplet, "handleStickEvents", + new Class[]{ GStick.class, GEvent.class }, + new String[]{ "stick", "event" } + ); + registeredMethods = DRAW_METHOD | MOUSE_METHOD ; + cursorOver = HAND; + G4P.addControl(this); + } + + /** + * Sets the stick mode to either 4 or 8 directions.
+ * If the mode parameter should be either G4P.X4 or G4P.X8 any + * other value will be silently ignored + * + * @param m the new mode + */ + public void setMode(int m){ + if(m != mode && m == X4 || m == X8){ + mode = m; + bufferInvalid = true; + } + } + + /** + * Get the current mode + * @return + */ + public int getMode(){ + return mode; + } + + /** + * Returns the current position of the stick based on
+ *
+	 *     5   6   7
+	 * 		\  |  /
+	 *       \ | /
+	 *   4 --- + --- 0         + is the dea zone so -1
+	 *       / | \
+	 *      /  |  \
+	 *     3   2   1
+	 * 

+ * @return current stick direction + */ + public int getPosition(){ + return position; + } + + /** + * Get the X position of the stick from
+ * *
+	 * X= -1   0   +1 
+	 * 		\  |  /  -1
+	 *       \ | /
+	 *     --- + ---  0
+	 *       / | \
+	 *      /  |  \
+	 *           Y=  +1
+	 * 

+ * @return the X value (-1, 0 or 1) + */ + public int getStickX(){ + return (position < 0) ? 0 : posX[position]; + } + + /** + * Get the Y position of the stick from
+ * *
+	 * X= -1   0   +1 
+	 * 		\  |  /  -1
+	 *       \ | /
+	 *     --- + ---  0
+	 *       / | \
+	 *      /  |  \
+	 *           Y=  +1
+	 * 

+ * @return the Y value (-1, 0 or 1) + */ + public int getStickY(){ + return (position < 0) ? 0 : posY[position]; + } + + /** + * Calculate the angle to the knob centre making sure it is in + * the range 0-360 + * @param px relative to centre + * @param py relative to centre + * @return the angle made by the stick + */ + protected float calcStickAngle(float px, float py){ + float a = PApplet.atan2(py, px); + if(a < 0) + a += PApplet.TWO_PI; + return a; + } + + /** + * Calculate the position (mode dependent) from an angle in the range 0-2PI + * @param a the angle 0-2PI + * @return direction (0-7) + */ + protected int getPositionFromAngle(float a){ + int newState; + if(mode == 1){ + a = (a + RAD45) % PApplet.TWO_PI; + newState = 2 * (int)(a / RAD90); + } + else { + a = (a + RAD22_5) % PApplet.TWO_PI; + newState = (int)(a / RAD45); + } + return newState % 8; + } + + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + // Make ox and oy relative to the centre of the stick + ox -= width/2; + oy -= height/2; + + // currSpot == 1 for text display area + if(currSpot >= 0 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(focusIsWith != this && currSpot > -1 && z > focusObjectZ()){ + status = PRESS_CONTROL; + position = -1; + rodLength = PApplet.sqrt(ox*ox + oy*oy); + stickAngle = calcStickAngle(ox, oy); + dragging = false; + takeFocus(); + bufferInvalid = true; + } + break; + case MouseEvent.RELEASE: + if(focusIsWith == this){ + loseFocus(null); + } + // If we are not already near the centre then make it so + // and fire an event + if(position != -1){ + position = -1; + fireEvent(this, GEvent.CHANGED); + } + hotspots[0].adjust(width/2, height/2); + rodLength = stickAngle = 0; + dragging = false; + status = OFF_CONTROL; + bufferInvalid = true; + break; + case MouseEvent.DRAG: + if(focusIsWith == this){ + status = DRAG_CONTROL; + dragging = true; + rodLength = PApplet.sqrt(ox*ox + oy*oy); + stickAngle = calcStickAngle(ox, oy); + int newPosition = -1; + if(rodLength >= actionRad){ + newPosition = getPositionFromAngle(stickAngle); + } + if(rodLength > actionRadLimit){ + ox = actionRadLimit * PApplet.cos(stickAngle); + oy = actionRadLimit * PApplet.sin(stickAngle); + rodLength = actionRadLimit; + } + hotspots[0].adjust(ox + width/2, oy + height/2); + if(newPosition != position){ + position = newPosition; + fireEvent(this, GEvent.CHANGED); + } + bufferInvalid = true; + } + break; + case MouseEvent.MOVE: + int currStatus = status; + // If dragged state will stay as PRESSED + if(currSpot == 1) + status = OVER_CONTROL; + else + status = OFF_CONTROL; + if(currStatus != status) + bufferInvalid = true; + break; + } + } + + public void draw(){ + if(!visible) return; + + // Update buffer if invalid + updateBuffer(); + winApp.pushStyle(); + + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + winApp.popMatrix(); + + winApp.popStyle(); + } + + protected void updateBuffer(){ + if(bufferInvalid) { + bufferInvalid = false; + buffer.beginDraw(); + // Back ground colour + if(opaque == true) + buffer.background(palette[BACK]); + else + buffer.background(buffer.color(255,0)); + // Move origin to centre + + buffer.translate(width/2, height/2); + + buffer.fill(palette[OUTERRING]); + buffer.stroke(palette[BORDERS]); + buffer.strokeWeight(1.0f); + buffer.ellipse(0,0,2*ledRingRad, 2*ledRingRad); + buffer.ellipse(0,0,2*actionRad, 2*actionRad); + // Draw everything except the stick + buffer.pushMatrix(); + int led = 0x00000001, delta = 2/mode; + for(int i = 0; i < 8; i += delta){ + buffer.stroke(palette[BORDERS]); + buffer.strokeWeight(1.0f); + buffer.line(0,0,ledRingRad,0); + // Only draw LEDs on even directions + if(i%2 == 0){ + buffer.noStroke(); + if(position >= 0 && (posMap[position] & led) == led) + buffer.fill(palette[LED_ACTIVE]); + else + buffer.fill(palette[LED_INACTIVE]); + buffer.ellipse(ledRingRad,0,ledWidth,ledHeight); + } + led <<= delta; + buffer.rotate(delta * RAD45); + } + buffer.popMatrix(); + + // Draw the inactive area near the centre of the + buffer.fill(palette[ACTIONRING]); + buffer.stroke(palette[BORDERS]); + buffer.strokeWeight(1.0f); + buffer.ellipse(0,0,2*actionRad, 2*actionRad); + + // Draw the rod and button + buffer.pushMatrix(); + buffer.rotate(stickAngle); + buffer.noStroke(); + buffer.fill(palette[ROD]); + buffer.ellipse(0,0,2*rodRad,2*rodRad); + buffer.rect(0,-rodRad,rodLength,2*rodRad); + buffer.strokeWeight(1); + buffer.stroke(palette[ROD]); + buffer.fill(palette[STICK_TOP_DRAG]); + // Draw thumb + switch(status){ + case OFF_CONTROL: + buffer.fill(palette[STICK_TOP]); + break; + case OVER_CONTROL: + buffer.fill(palette[STICK_TOP_OVER]); + break; + case PRESS_CONTROL: + buffer.fill(palette[STICK_TOP_PRESS]); + break; + case DRAG_CONTROL: + buffer.fill(palette[STICK_TOP_DRAG]); + break; + } + buffer.ellipse(rodLength,0,2*gripRad, 2*gripRad); + buffer.popMatrix(); + buffer.endDraw(); + } + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTabManager.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTabManager.java new file mode 100644 index 0000000..d0e4a41 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTabManager.java @@ -0,0 +1,127 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.util.LinkedList; + +/** + * Allows TABBING between text controls. + * A tab manager allows the user to use the TAB key to move from one text control + * (GTextField or GTextArea) to the another. This is useful when creating a 'form' + * made from several text controls.
+ * The tab order is decided by the order the text controls are added to the tab + * manager. The TAB key move the focus forwards and SHIFT+TAB moves it backwards. + * Note that tabbing is not cyclic so pressing TAB on the last control does not + * set the focus on the first control, in fact it will be ignored. Similar + * logic applies to SHIFT_TAB on the first control
+ * At least 2 controls must be added to the tab manager. + * + * @author Peter Lager + * + */ +public class GTabManager { + + private LinkedList textControls; + + public GTabManager(){ + textControls = new LinkedList(); + } + + /** + * Attempt to add multiple controls to the tab manager. The tab order is determined + * by their order as parameters to this method. + * + * @param controls a comma separated list of text field or text area controls. + * @return true if any or all of the controls were added and false if none were added. + */ + public boolean addControls(GEditableTextControl... controls){ + boolean result = false; + for(GEditableTextControl control : controls) + result |= addControl(control); + return result; + } + + /** + * Add the next text control to this tab manager. + * + * @param control to add + * @return true if added successfully + */ + public boolean addControl(GEditableTextControl control){ + if(!textControls.contains(control)){ + control.tabManager = this; + textControls.addLast(control); + return true; + } + return false; + } + + /** + * Remove a control from the tab manager. This does not affect the tab + * order of the remaining controls. + * + * @param control + * @return true if remove successfully + */ + public boolean removeControl(GEditableTextControl control){ + int index = textControls.lastIndexOf(control); + if(index > 0){ + control.tabManager = null; + textControls.remove(index); + return true; + } + return false; + } + + /** + * Used when the tab key is pressed to move to the next control + * @param control + * @return true if it found a next control else false + */ + boolean nextControl(GEditableTextControl control){ + int index = textControls.lastIndexOf(control); + if(textControls.size() > 1 && index >= 0 && index < textControls.size() - 1){ + index++; + GAbstractControl.controlToTakeFocus = textControls.get(index);; + return true; + } + return false; + } + + /** + * Used when the shift+tab key is pressed to move to the previous control + * @param control + * @return true if it found a previous control else false + */ + boolean prevControl(GEditableTextControl control){ + int index = textControls.lastIndexOf(control); + if(textControls.size() > 1 && index > 0){ + index--; + GAbstractControl.controlToTakeFocus = textControls.get(index); + return true; + } + return false; + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextAlign.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextAlign.java new file mode 100644 index 0000000..0bc5e58 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextAlign.java @@ -0,0 +1,108 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.awt.Font; + +import processing.core.PApplet; + + +/** + * + * Is the basis for all classes that have some simple non-editable text element to them.
+ * + * @author Peter Lager + * + */ +public abstract class GTextAlign extends GTextBase { + + protected GAlign textAlignH = GAlign.CENTER, textAlignV = GAlign.MIDDLE; + + protected float stX, stY; + + public GTextAlign(PApplet theApplet, float p0, float p1, float p2, float p3) { + super(theApplet, p0, p1, p2, p3); + } + + /** + * Set the horizontal and/or vertical text alignment. Use the constants in GAlign + * e.g.
GAlign.LEFT

+ * + * If you want to set just one of these then pass null in the other + * + * @param horz LEFT, CENTER, RIGHT or JUSTIFY + * @param vert TOP, MIDDLE, BOTTOM + */ + public void setTextAlign(GAlign horz, GAlign vert){ + if(horz != null && horz.isHorzAlign()){ + textAlignH = horz; + stext.setJustify(textAlignH == GAlign.JUSTIFY); + } + if(vert != null && vert.isVertAlign()){ + textAlignV = vert; + } + bufferInvalid = true; + } + + /** + * Combines setting the text and text alignment in one method.
+ * + * If you want to set just one of the alignments then pass null + * in the other. + * + * @param text + * @param horz LEFT, CENTER, RIGHT or JUSTIFY + * @param vert TOP, MIDDLE, BOTTOM + */ + public void setText(String text, GAlign horz, GAlign vert){ + setText(text); + setTextAlign(horz, vert); + bufferInvalid = true; + } + + protected void calcAlignment(){ + switch(textAlignH){ + case RIGHT: + stX = width - stext.getWrapWidth() - TPAD; + break; + case LEFT: + case CENTER: + case JUSTIFY: + default: + stX = TPAD; + } + switch(textAlignV){ + case TOP: + stY = TPAD; + break; + case BOTTOM: + stY = height - stext.getTextAreaHeight() - TPAD; + break; + case MIDDLE: + default: + stY = (height - stext.getTextAreaHeight()) / 2; + } + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextArea.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextArea.java new file mode 100644 index 0000000..cddb9e3 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextArea.java @@ -0,0 +1,765 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2012 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSrect; +import g4p_controls.StyledString.TextLayoutHitInfo; +import g4p_controls.StyledString.TextLayoutInfo; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.font.TextHitInfo; +import java.awt.font.TextLayout; +import java.awt.geom.GeneralPath; +import java.util.LinkedList; + +import processing.core.PApplet; +import processing.core.PGraphics; +import processing.core.PGraphicsJava2D; +import processing.event.MouseEvent; + +/** + * The text area component.
+ * + * This control allows the user to enter and edit multiple lines of text. The control + * also allows default text, horizontal and vertical scrollbars. + * + * Enables user text input at runtime. Text can be selected using the mouse + * or keyboard shortcuts and then copied or cut to the clipboard. Text + * can also be pasted in. + * + * @author Peter Lager + * + */ +public class GTextArea extends GEditableTextControl { + + private static float pad = 6; + + /** + * Create a text area without scrollbars and a text wrap width to fit the control. + * + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + */ + public GTextArea(PApplet theApplet, float p0, float p1, float p2, float p3) { + this(theApplet, p0, p1, p2, p3, SCROLLBARS_NONE, Integer.MAX_VALUE); + } + + /** + * Create a text field with the given scrollbar policy and a text wrap width to fit the control.
+ * The scrollbar policy can be one of these
+ *
    + *
  • SCROLLBARS_NONE
  • + *
  • SCROLLBARS_HORIZONTAL_ONLY
  • + *
  • SCROLLBARS_VERTICAL_ONLY
  • + *
  • SCROLLBARS_BOTH
  • + *
+ * If you want the scrollbar to auto hide then perform a logical or with + *
    + *
  • SCROLLBARS_AUTOHIDE
  • + *
+ * e.g. SCROLLBARS_BOTH | SCROLLBARS_AUTOHIDE + *
+ * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + * @param sbPolicy + */ + public GTextArea(PApplet theApplet, float p0, float p1, float p2, float p3, int sbPolicy) { + this(theApplet, p0, p1, p2, p3, sbPolicy, Integer.MAX_VALUE); + } + + /** + * Create a text field with the given scrollbar policy with a user specified text wrap length
+ * + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + * @param sbPolicy + */ + public GTextArea(PApplet theApplet, float p0, float p1, float p2, float p3, int sbPolicy, int wrapWidth) { + super(theApplet, p0, p1, p2, p3, sbPolicy); + children = new LinkedList(); + tx = ty = pad; + tw = width - 2 * pad - ((scrollbarPolicy & SCROLLBAR_VERTICAL) != 0 ? 18 : 0); + th = height - 2 * pad - ((scrollbarPolicy & SCROLLBAR_HORIZONTAL) != 0 ? 18 : 0); + this.wrapWidth = (wrapWidth == Integer.MAX_VALUE) ? (int)tw : wrapWidth; + // Clip zone + gpTextDisplayArea = new GeneralPath(); + gpTextDisplayArea.moveTo( 0, 0); + gpTextDisplayArea.lineTo( 0, th); + gpTextDisplayArea.lineTo(tw, th); + gpTextDisplayArea.lineTo(tw, 0); + gpTextDisplayArea.closePath(); + + // The image buffer is just for the typing area + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.rectMode(PApplet.CORNER); + buffer.g2.setFont(localFont); + hotspots = new HotSpot[]{ + new HSrect(1, tx, ty, tw, th), // typing area + new HSrect(9, 0, 0, width, height) // control surface + }; + G4P.pushStyle(); + G4P.showMessages = false; + z = Z_STICKY; + G4P.control_mode = GControlMode.CORNER; + if((scrollbarPolicy & SCROLLBAR_HORIZONTAL) != 0){ + hsb = new GScrollbar(theApplet, 0, 0, tw, 16); + addControl(hsb, tx, ty + th + 2, 0); + hsb.addEventHandler(this, "hsbEventHandler"); + hsb.setAutoHide(autoHide); + } + if((scrollbarPolicy & SCROLLBAR_VERTICAL) != 0){ + vsb = new GScrollbar(theApplet, 0, 0, th, 16); + addControl(vsb, tx + tw + 18, ty, PI/2); + vsb.addEventHandler(this, "vsbEventHandler"); + vsb.setAutoHide(autoHide); + } + G4P.popStyle(); + setText("", (int)tw); +// z = Z_STICKY; + createEventHandler(G4P.sketchApplet, "handleTextEvents", + new Class[]{ GEditableTextControl.class, GEvent.class }, + new String[]{ "textcontrol", "event" } + ); + registeredMethods = PRE_METHOD | DRAW_METHOD | MOUSE_METHOD | KEY_METHOD; + G4P.addControl(this); + } + + /** + * Set the default text for this control. If provided this text will be + * displayed in italic whenever it is empty. It will also be wrapped to the control size. + * @param dtext + */ + public void setDefaultText(String dtext){ + if(dtext == null || dtext.length() == 0) + defaultText = null; + else { + defaultText = new StyledString(dtext, wrapWidth); + defaultText.addAttribute(G4P.POSTURE, G4P.POSTURE_OBLIQUE); + } + bufferInvalid = true; + } + + /** + * Set the text to be used. The wrap width is determined by the size + * of the control. + * @param text + */ + public void setText(String text){ + //setText(text, wrapWidth); + setStyledText(new StyledString(text, wrapWidth)); + } + + /** + * Set the text to display and adjust any scrollbars + * @param text + * @param wrapWidth + */ + public void setText(String text, int wrapWidth){ + this.wrapWidth = wrapWidth; + setStyledText(new StyledString(text, wrapWidth)); + } + + public void setStyledText(StyledString st){ + stext = st; + if(stext.getWrapWidth() == Integer.MAX_VALUE) + stext.setWrapWidth(wrapWidth); + else + wrapWidth = stext.getWrapWidth(); + stext.getLines(buffer.g2); + if(stext.getNbrLines() > 0){ + + // Ray: modify endTLHK.tli to set the cursor on the + // newly added line. + //endTLHI.tli = stext.getLines(buffer.g2).getFirst(); + endTLHI.tli = stext.getLines(buffer.g2).getLast(); + + endTLHI.thi = endTLHI.tli.layout.getNextLeftHit(1); + startTLHI.copyFrom(endTLHI); + calculateCaretPos(endTLHI); + keepCursorInView = true; + } + ptx = pty = 0; + float sTextHeight; + if(vsb != null){ + sTextHeight = stext.getTextAreaHeight(); + if(sTextHeight < th) + vsb.setValue(0.0f, 1.0f); + else + vsb.setValue(0, th/sTextHeight); + } + // If needed update the horizontal scrollbar + if(hsb != null){ + if(stext.getMaxLineLength() < tw) + hsb.setValue(0,1); + else + hsb.setValue(0, tw/stext.getMaxLineLength()); + } + bufferInvalid = true; + } + + /** + * Add text to the end of the current text. This is useful for a logging' type activity. + * + * @param extraText the text to append + */ + public void appendText(String extraText){ + if(extraText == null || extraText.equals("")) + return; + + // Ray: avoid inserting a new line when calling appendText + //if(stext.insertCharacters(stext.length(), extraText, true) == 0) + if(stext.insertCharacters(stext.length(), extraText, false) == 0) + return; + + LinkedList lines = stext.getLines(buffer.g2); + endTLHI.tli = lines.getLast(); + + // Ray: modify endTLHI.thi to set cursor to the beginning of the new line + //endTLHI.thi = endTLHI.tli.layout.getNextRightHit(endTLHI.tli.nbrChars - 1); + endTLHI.thi = endTLHI.tli.layout.getNextLeftHit(1); + + startTLHI.copyFrom(endTLHI); + calculateCaretPos(endTLHI); + if(vsb != null){ + float vfiller = Math.min(1, th/stext.getTextAreaHeight()); + vsb.setValue(1 - vfiller, vfiller); + keepCursorInView = true; + } + // If needed update the horizontal scrollbar + if(hsb != null){ + float hvalue = lines.getLast().layout.getVisibleAdvance(); + float hlinelength = stext.getMaxLineLength(); + float hfiller = Math.min(1, tw/hlinelength); + if(caretX < tw) + hsb.setValue(0,hfiller); + else + hsb.setValue(hvalue/hlinelength, hfiller); + keepCursorInView = true; + } + bufferInvalid = true; + } + + /** + * Add text to the end of the current text. This is useful for a logging' type activity. + * + * @param extraText the text to append + */ +// public void appendText(String extraText){ +// appendText(extraText, false); +// } + + /** + * If the buffer is invalid then redraw it. + */ + protected void updateBuffer(){ + if(bufferInvalid) { + Graphics2D g2d = buffer.g2; + // Get the latest lines of text + LinkedList lines = stext.getLines(g2d); + if(lines.isEmpty() && defaultText != null) + lines = defaultText.getLines(g2d); + + bufferInvalid = false; + + TextLayoutHitInfo startSelTLHI = null, endSelTLHI = null; + buffer.beginDraw(); + // Whole control surface if opaque + if(opaque) + buffer.background(palette[6]); + else + buffer.background(buffer.color(255,0)); + + // Now move to top left corner of text display area + buffer.translate(tx,ty); + + // Typing area surface + buffer.noStroke(); + buffer.fill(palette[7]); + buffer.rect(-1,-1,tw+2,th+2); + + g2d.setClip(gpTextDisplayArea); + buffer.translate(-ptx, -pty); + // Translate in preparation for display selection and text + if(hasSelection()){ + if(endTLHI.compareTo(startTLHI) == -1){ + startSelTLHI = endTLHI; + endSelTLHI = startTLHI; + } + else { + startSelTLHI = startTLHI; + endSelTLHI = endTLHI; + } + } + + // Display selection and text + for(TextLayoutInfo lineInfo : lines){ + TextLayout layout = lineInfo.layout; + buffer.translate(0, layout.getAscent()); + // Draw selection if any + if(hasSelection() && lineInfo.compareTo(startSelTLHI.tli) >= 0 && lineInfo.compareTo(endSelTLHI.tli) <= 0 ){ + int ss = 0; + ss = (lineInfo.compareTo(startSelTLHI.tli) == 0) ? startSelTLHI.thi.getInsertionIndex() : 0; + int ee = endSelTLHI.thi.getInsertionIndex(); + ee = (lineInfo.compareTo(endSelTLHI.tli) == 0) ? endSelTLHI.thi.getInsertionIndex() : lineInfo.nbrChars-1; + g2d.setColor(jpalette[14]); + Shape selShape = layout.getLogicalHighlightShape(ss, ee); + g2d.fill(selShape); + } + // display text + g2d.setColor(jpalette[2]); + lineInfo.layout.draw(g2d, 0, 0); + buffer.translate(0, layout.getDescent() + layout.getLeading()); + } + g2d.setClip(null); + buffer.endDraw(); + } + } + + public void pre(){ + if(keepCursorInView){ + boolean horzScroll = false, vertScroll = false; + float max_ptx = caretX - tw + 2; + float max_pty = caretY - th + 2 * stext.getMaxLineHeight(); + + if(endTLHI != null){ + if(ptx > caretX){ // LEFT? + ptx -= HORZ_SCROLL_RATE; + if(ptx < 0) ptx = 0; + horzScroll = true; + } + else if(ptx < max_ptx){ // RIGHT? + ptx += HORZ_SCROLL_RATE; + if(ptx > max_ptx) ptx = max_ptx; + horzScroll = true; + } + if(pty > caretY){ // UP? + pty -= VERT_SCROLL_RATE; + if(pty < 0) pty = 0; + vertScroll = true; + } + else if(pty < max_pty){ // DOWN? + pty += VERT_SCROLL_RATE; + vertScroll = true; + } + if(horzScroll && hsb != null) + hsb.setValue(ptx / (stext.getMaxLineLength() + 4)); + if(vertScroll && vsb != null) + vsb.setValue(pty / (stext.getTextAreaHeight() + 1.5f * stext.getMaxLineHeight())); + } + // If we have scrolled invalidate the buffer otherwise forget it + if(horzScroll || vertScroll) + bufferInvalid = true; + else + keepCursorInView = false; + } + } + + public void draw(){ + if(!visible) return; + + // Update buffer if invalid + updateBuffer(); + winApp.pushStyle(); + + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + + winApp.pushMatrix(); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + + // Draw caret if text display area + if(focusIsWith == this && showCaret && endTLHI != null){ + float[] cinfo = endTLHI.tli.layout.getCaretInfo(endTLHI.thi); + float x_left = - ptx + cinfo[0]; + float y_top = - pty + endTLHI.tli.yPosInPara; + float y_bot = y_top - cinfo[3] + cinfo[5]; + if(x_left >= 0 && x_left <= tw && y_top >= 0 && y_bot <= th){ + winApp.strokeWeight(1.9f); + winApp.stroke(palette[15]); + winApp.line(tx+x_left, ty+Math.max(0, y_top), tx+x_left, ty+Math.min(th, y_bot)); + } + } + + winApp.popMatrix(); + // Draw scrollbars + if(children != null){ + for(GAbstractControl c : children) + c.draw(); + } + winApp.popMatrix(); + winApp.popStyle(); + } + + public PGraphics getSnapshot(){ + updateBuffer(); + PGraphicsJava2D snap = (PGraphicsJava2D) winApp.createGraphics(buffer.width, buffer.height, PApplet.JAVA2D); + snap.beginDraw(); + snap.image(buffer,0,0); + if(hsb != null){ + snap.pushMatrix(); + snap.translate(hsb.getX(), hsb.getY()); + snap.image(hsb.getBuffer(), 0, 0); + snap.popMatrix(); + } + if(vsb != null){ + snap.pushMatrix(); + snap.translate(vsb.getX(), vsb.getY()); + snap.rotate(PApplet.PI/2); + snap.image(vsb.getBuffer(), 0, 0); + snap.popMatrix(); + } + snap.endDraw(); + return snap; + } + + protected void keyPressedProcess(int keyCode, char keyChar, boolean shiftDown, boolean ctrlDown){ + boolean validKeyCombo = true; + + switch(keyCode){ + case LEFT: + moveCaretLeft(endTLHI); + break; + case RIGHT: + moveCaretRight(endTLHI); + break; + case UP: + moveCaretUp(endTLHI); + break; + case DOWN: + moveCaretDown(endTLHI); + break; + case GConstants.HOME: + if(ctrlDown) // move to start of text + moveCaretStartOfText(endTLHI); + else // Move to start of line + moveCaretStartOfLine(endTLHI); + break; + case GConstants.END: + if(ctrlDown) // move to end of text + moveCaretEndOfText(endTLHI); + else // Move to end of line + moveCaretEndOfLine(endTLHI); + break; + case 'A': + if(ctrlDown){ + moveCaretStartOfText(startTLHI); + moveCaretEndOfText(endTLHI); + // Make shift down so that the start caret position is not + // moved to match end caret position. + shiftDown = true; + } + break; + case 'C': + if(ctrlDown) + GClip.copy(getSelectedText()); + validKeyCombo = false; + break; + case 'V': + if(ctrlDown){ + String p = GClip.paste(); + if(p.length() > 0){ + // delete selection and add + if(hasSelection()) + stext.deleteCharacters(pos, nbr); + stext.insertCharacters(pos, p); + adjust = p.length(); + textChanged = true; + } + } + break; + default: + validKeyCombo = false; + } + + if(validKeyCombo){ + calculateCaretPos(endTLHI); + //**************************************************************** + // If we have moved to the end of a paragraph marker + if(caretX > stext.getWrapWidth()){ + switch(keyCode){ + case LEFT: + case UP: + case DOWN: + case END: + moveCaretLeft(endTLHI); + validKeyCombo = true; + break; + case RIGHT: + if(!moveCaretRight(endTLHI)) + moveCaretLeft(endTLHI); + validKeyCombo = true; + } + // Calculate new caret position + // calculateCaretPos(startTLHI); + calculateCaretPos(endTLHI); + } + //**************************************************************** + + calculateCaretPos(endTLHI); + + if(!shiftDown) + startTLHI.copyFrom(endTLHI); + bufferInvalid = true; + } + } + + protected void keyTypedProcess(int keyCode, char keyChar, boolean shiftDown, boolean ctrlDown){ + int ascii = (int)keyChar; + + if(ascii >= 32 && ascii < 127){ + if(hasSelection()) + stext.deleteCharacters(pos, nbr); + stext.insertCharacters(pos, "" + keyChar); + adjust = 1; textChanged = true; + } + else if(keyChar == BACKSPACE){ + if(hasSelection()){ + stext.deleteCharacters(pos, nbr); + adjust = 0; textChanged = true; + } + else if(stext.deleteCharacters(pos - 1, 1)){ + adjust = -1; textChanged = true; + } + } + else if(keyChar == DELETE){ + if(hasSelection()){ + stext.deleteCharacters(pos, nbr); + adjust = 0; textChanged = true; + } + else if(stext.deleteCharacters(pos, 1)){ + adjust = 0; textChanged = true; + } + } + else if(keyChar == ENTER || keyChar == RETURN) { + if(stext.insertEOL(pos)){ + adjust = 1; textChanged = true; + newline = true; + } + } + else if(keyChar == TAB){ + // If possible move to next text control + if(tabManager != null){ + boolean result = (shiftDown) ? tabManager.prevControl(this) : tabManager.nextControl(this); + if(result){ + startTLHI.copyFrom(endTLHI); + return; + } + } + } + // If we have emptied the text then recreate a one character string (space) + if(stext.length() == 0){ + stext.insertCharacters(0, " "); + adjust++; textChanged = true; + } + } + /** + * Move caret to home position + * @return true if caret moved else false + */ + protected boolean moveCaretStartOfLine(TextLayoutHitInfo currPos){ + if(currPos.thi.getCharIndex() == 0) + return false; // already at start of line + currPos.thi = currPos.tli.layout.getNextLeftHit(1); + return true; + } + + protected boolean moveCaretEndOfLine(TextLayoutHitInfo currPos){ + if(currPos.thi.getCharIndex() == currPos.tli.nbrChars - 1) + return false; // already at end of line + currPos.thi = currPos.tli.layout.getNextRightHit(currPos.tli.nbrChars - 1); + return true; + } + + protected boolean moveCaretStartOfText(TextLayoutHitInfo currPos){ + if(currPos.tli.lineNo == 0 && currPos.thi.getCharIndex() == 0) + return false; // already at start of text + currPos.tli = stext.getTLIforLineNo(0); + currPos.thi = currPos.tli.layout.getNextLeftHit(1); + return true; + } + + protected boolean moveCaretEndOfText(TextLayoutHitInfo currPos){ + if(currPos.tli.lineNo == stext.getNbrLines() - 1 && currPos.thi.getCharIndex() == currPos.tli.nbrChars - 1) + return false; // already at end of text + currPos.tli = stext.getTLIforLineNo(stext.getNbrLines() - 1); + currPos.thi = currPos.tli.layout.getNextRightHit(currPos.tli.nbrChars - 1); + return true; + } + + protected boolean moveCaretUp(TextLayoutHitInfo currPos){ + if(currPos.tli.lineNo == 0) + return false; + TextLayoutInfo ntli = stext.getTLIforLineNo(currPos.tli.lineNo - 1); + TextHitInfo nthi = ntli.layout.hitTestChar(caretX, 0); + currPos.tli = ntli; + currPos.thi = nthi; + return true; + } + + protected boolean moveCaretDown(TextLayoutHitInfo currPos){ + if(currPos.tli.lineNo == stext.getNbrLines() - 1) + return false; + TextLayoutInfo ntli = stext.getTLIforLineNo(currPos.tli.lineNo + 1); + TextHitInfo nthi = ntli.layout.hitTestChar(caretX, 0); + currPos.tli = ntli; + currPos.thi = nthi; + return true; + } + + /** + * Move caret left by one character. If necessary move to the end of the line above + * @return true if caret was moved else false + */ + protected boolean moveCaretLeft(TextLayoutHitInfo currPos){ + TextLayoutInfo ntli; + TextHitInfo nthi = currPos.tli.layout.getNextLeftHit(currPos.thi); + if(nthi == null){ + // Move the caret to the end of the previous line + if(currPos.tli.lineNo == 0) + // Can't goto previous line because this is the first line + return false; + else { + // Move to end of previous line + ntli = stext.getTLIforLineNo(currPos.tli.lineNo - 1); + nthi = ntli.layout.getNextRightHit(ntli.nbrChars-1); + currPos.tli = ntli; + currPos.thi = nthi; + } + } + else { + // Move the caret to the left of current position + currPos.thi = nthi; + } + return true; + } + + /** + * Move caret right by one character. If necessary move to the start of the next line + * @return true if caret was moved else false + */ + protected boolean moveCaretRight(TextLayoutHitInfo currPos){ + TextLayoutInfo ntli; + TextHitInfo nthi = currPos.tli.layout.getNextRightHit(currPos.thi); + if(nthi == null){ + // Move the caret to the start of the next line the previous line + if(currPos.tli.lineNo >= stext.getNbrLines() - 1) + // Can't goto next line because this is the last line + return false; + else { + // Move to start of next line + ntli = stext.getTLIforLineNo(currPos.tli.lineNo + 1); + nthi = ntli.layout.getNextLeftHit(1); + currPos.tli = ntli; + currPos.thi = nthi; + } + } + else { + // Move the caret to the right of current position + currPos.thi = nthi; + } + return true; + } + + /** + * Will respond to mouse events. + */ + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + ox -= tx; oy -= ty; // Remove translation + + currSpot = whichHotSpot(ox, oy); + + if(currSpot == 1 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(currSpot == 1){ + if(focusIsWith != this && z >= focusObjectZ()){ + keepCursorInView = true; + takeFocus(); + } + dragging = false; + if(stext == null || stext.length() == 0){ + stext = new StyledString(" ", wrapWidth); + stext.getLines(buffer.g2); + } + endTLHI = stext.calculateFromXY(buffer.g2, ox + ptx, oy + pty); + startTLHI = new TextLayoutHitInfo(endTLHI); + calculateCaretPos(endTLHI); + bufferInvalid = true; + } + else { // Not over this control so if we have focus loose it + if(focusIsWith == this) + loseFocus(null); + } + break; + case MouseEvent.RELEASE: + dragging = false; + bufferInvalid = true; + break; + case MouseEvent.DRAG: + if(focusIsWith == this){ + keepCursorInView = true; + dragging = true; + endTLHI = stext.calculateFromXY(buffer.g2, ox + ptx, oy + pty); + calculateCaretPos(endTLHI); + fireEvent(this, GEvent.SELECTION_CHANGED); + bufferInvalid = true; + } + break; + } + } + + protected void calculateCaretPos(TextLayoutHitInfo tlhi){ + float temp[] = tlhi.tli.layout.getCaretInfo(tlhi.thi); + caretX = temp[0]; + caretY = tlhi.tli.yPosInPara; + } + + + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextBase.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextBase.java new file mode 100644 index 0000000..647f35f --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextBase.java @@ -0,0 +1,211 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2013 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.awt.Font; +import java.awt.font.TextAttribute; + +import processing.core.PApplet; + +/** + * Base class for any control that uses styled text. + * + * @author Peter Lager + * + */ +public abstract class GTextBase extends GAbstractControl { + + protected static final int TPAD = 2; + protected static final int TPAD2 = TPAD * 2; + protected static final int TPAD4 = TPAD * 4; + + + /** The styled text used by this control */ + public StyledString stext = null; + + protected Font localFont = G4P.globalFont; + + + /** + * Constructor + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + */ + public GTextBase(PApplet theApplet, float p0, float p1, float p2, float p3) { + super(theApplet, p0, p1, p2, p3); + } + + /** + * Set the text to be displayed. + * + * @param text + */ + public void setText(String text){ + if(text == null || text.length() == 0 ) + text = " "; + stext = new StyledString(text, (int)width - TPAD2); + bufferInvalid = true; + } + + /** + * Load the styled string to be used by this control. + * + * @param fname the name of the file to use + * @return true if loaded successfully else false + */ + public boolean loadText(String fname){ + StyledString ss = StyledString.load(winApp, fname); + if(ss != null){ + setStyledText(ss); + stext.startIdx = stext.endIdx = -1; + bufferInvalid = true; + return true; + } + return false; + } + + /** + * Save the styled text used by this control to file. + * + * @param fname the name of the file to use + * @return true if saved successfully else false + */ + public boolean saveText(String fname){ + if(stext != null){ + stext.startIdx = stext.endIdx = -1; + StyledString.save(winApp, stext, fname); + return true; + } + return false; + } + + /** + * Set the font to be used in this control + * + * @param font AWT font to use + */ + public void setFont(Font font) { + if(font != null && font != localFont && buffer != null){ + localFont = font; + buffer.g2.setFont(localFont); + bufferInvalid = true; + } + } + + /** + * Allows the user to provide their own styled text for this component + * @param ss + */ + public void setStyledText(StyledString ss){ + if(ss != null) { + stext = ss; + stext.setWrapWidth((int)width - TPAD2); + bufferInvalid = true; + } + } + + /** + * Clear all applied styles from the whole text. + */ + public void setTextPlain(){ + stext.clearAllAttributes(); + bufferInvalid = true; + } + + /** + * Make the selected characters bold.
+ * Characters affected are >= start and < end + * + * @param start the first character to style + * @param end the first character not to style + */ + public void setTextBold(int start, int end){ + addAttributeImpl(G4P.WEIGHT, G4P.WEIGHT_BOLD, start, end); + } + + /** + * Make all the characters bold. + */ + public void setTextBold(){ + addAttributeImpl(G4P.WEIGHT, G4P.WEIGHT_BOLD); + } + + /** + * Make the selected characters italic.
+ * Characters affected are >= start and < end + * + * @param start the first character to style + * @param end the first character not to style + */ + public void setTextItalic(int start, int end){ + addAttributeImpl(G4P.POSTURE, G4P.POSTURE_OBLIQUE, start, end); + } + + /** + * Make all the characters italic. + */ + public void setTextItalic(){ + addAttributeImpl(G4P.POSTURE, G4P.POSTURE_OBLIQUE); + } + + /** + * Get the text used for this control. + * @return the displayed text without styling + */ + public String getText(){ + return stext.getPlainText(); + } + + /** + * Apply the style to the whole text + * + * @param style the style attribute + * @param value 'amount' to apply + */ + protected void addAttributeImpl(TextAttribute style, Object value){ + stext.addAttribute(style, value); + bufferInvalid = true; + } + + /** + * Apply the style to a portion of the strin + * + * @param style the style attribute + * @param value 'amount' to apply + * @param s first character to be included for styling + * @param e the first character not to be included for stylin + */ + protected void addAttributeImpl(TextAttribute style, Object value, int s, int e){ + if(s >= e) return; + if(s < 0) s = 0; + if(e > stext.length()) e = stext.length(); + stext.addAttribute(style, value, s, e); + bufferInvalid = true; + } + + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextField.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextField.java new file mode 100644 index 0000000..cb1e89e --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextField.java @@ -0,0 +1,516 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2012 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSrect; +import g4p_controls.StyledString.TextLayoutHitInfo; +import g4p_controls.StyledString.TextLayoutInfo; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.font.TextLayout; +import java.awt.geom.GeneralPath; +import java.util.LinkedList; + +import processing.core.PApplet; +import processing.core.PGraphics; +import processing.core.PGraphicsJava2D; +import processing.event.MouseEvent; + +/** + * The text field component.
+ * + * This control allows the user to enter and edit a single line of text. The control + * also allows default text and a horizontal scrollbar. + * + * can be created to manage either a single line of text or + * multiple lines of text.
+ * + * Enables user text input at runtime. Text can be selected using the mouse + * or keyboard shortcuts and then copied or cut to the clipboard. Text + * can also be pasted in. + * + * @author Peter Lager + * + */ +public class GTextField extends GEditableTextControl { + + /** + * Create a text field without a scrollbar. + * + * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + */ + public GTextField(PApplet theApplet, float p0, float p1, float p2, float p3) { + this(theApplet, p0, p1, p2, p3, SCROLLBARS_NONE); + } + + /** + * Create a text field with the given scrollbar policy.
+ * This policy can be one of these
+ *
    + *
  • SCROLLBARS_NONE
  • + *
  • SCROLLBARS_HORIZONTAL_ONLY
  • + *
+ * If you want the scrollbar to auto hide then perform a logical or with + *
    + *
  • SCROLLBARS_AUTOHIDE
  • + *
+ * e.g. SCROLLBARS_HORIZONTAL_ONLY | SCROLLBARS_AUTOHIDE + *
+ * @param theApplet + * @param p0 + * @param p1 + * @param p2 + * @param p3 + * @param sbPolicy + */ + public GTextField(PApplet theApplet, float p0, float p1, float p2, float p3, int sbPolicy) { + super(theApplet, p0, p1, p2, p3, sbPolicy); + children = new LinkedList(); + tx = ty = 2; + tw = width - 2 * 2; + th = height - ((scrollbarPolicy & SCROLLBAR_HORIZONTAL) != 0 ? 11 : 0); + wrapWidth = Integer.MAX_VALUE; + gpTextDisplayArea = new GeneralPath(); + gpTextDisplayArea.moveTo( 0, 0); + gpTextDisplayArea.lineTo( 0, th); + gpTextDisplayArea.lineTo(tw, th); + gpTextDisplayArea.lineTo(tw, 0); + gpTextDisplayArea.closePath(); + + // The image buffer is just for the typing area + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.rectMode(PApplet.CORNER); + buffer.g2.setFont(localFont); + hotspots = new HotSpot[]{ + new HSrect(1, tx, ty, tw, th), // typing area + new HSrect(9, 0, 0, width, height) // control surface + }; + + G4P.pushStyle(); + G4P.showMessages = false; + + z = Z_STICKY; + + G4P.control_mode = GControlMode.CORNER; + if((scrollbarPolicy & SCROLLBAR_HORIZONTAL) != 0){ + hsb = new GScrollbar(theApplet, 0, 0, tw, 10); + addControl(hsb, tx, ty + th + 2, 0); + hsb.addEventHandler(this, "hsbEventHandler"); + hsb.setAutoHide(autoHide); + } + G4P.popStyle(); + setText(""); +// z = Z_STICKY; + createEventHandler(G4P.sketchApplet, "handleTextEvents", + new Class[]{ GEditableTextControl.class, GEvent.class }, + new String[]{ "textcontrol", "event" } + ); + registeredMethods = PRE_METHOD | DRAW_METHOD | MOUSE_METHOD | KEY_METHOD; + G4P.addControl(this); + } + + /** + * Set the styled text for this textfield after ensuring that all EOL characters + * have been removed. + * @param ss + */ + public void setStyledText(StyledString ss){ + stext = ss.convertToSingleLineText(); + stext.getLines(buffer.g2); + if(stext.getNbrLines() > 0){ + endTLHI.tli = stext.getLines(buffer.g2).getFirst(); + endTLHI.thi = endTLHI.tli.layout.getNextLeftHit(1); + startTLHI.copyFrom(endTLHI); + calculateCaretPos(endTLHI); + keepCursorInView = true; + } + ptx = pty = 0; + // If needed update the horizontal scrollbar + if(hsb != null){ + if(stext.getMaxLineLength() < tw) + hsb.setValue(0,1); + else + hsb.setValue(0, tw/stext.getMaxLineLength()); + } + bufferInvalid = true; + } + + /** + * Set the plain text to be displayed. + * @param text plain text + */ + public void setText(String text){ + setStyledText(new StyledString(text, wrapWidth)); + } + + /** + * Add some plain text to the end of the existing text. + * + * @param extraText + */ + public void appendText(String extraText){ + if(extraText == null || extraText.equals("")) + return; + if(stext.insertCharacters(stext.length(), extraText) == 0) + return; +// text = stext.getPlainText(); + LinkedList lines = stext.getLines(buffer.g2); + endTLHI.tli = lines.getLast(); + endTLHI.thi = endTLHI.tli.layout.getNextRightHit(endTLHI.tli.nbrChars - 1); + startTLHI.copyFrom(endTLHI); + calculateCaretPos(endTLHI); + if(hsb != null){ + float hvalue = lines.getLast().layout.getVisibleAdvance(); + float hlinelength = stext.getMaxLineLength(); + float hfiller = Math.min(1, tw/hlinelength); + if(caretX < tw) + hsb.setValue(0,hfiller); + else + hsb.setValue(hvalue/hlinelength, hfiller); + keepCursorInView = true; + } + bufferInvalid = true; + } + + public PGraphics getSnapshot(){ + updateBuffer(); + PGraphicsJava2D snap = (PGraphicsJava2D) winApp.createGraphics(buffer.width, buffer.height, PApplet.JAVA2D); + snap.beginDraw(); + snap.image(buffer,0,0); + if(hsb != null){ + snap.pushMatrix(); + snap.translate(hsb.getX(), hsb.getY()); + snap.image(hsb.getBuffer(), 0, 0); + snap.popMatrix(); + } + snap.endDraw(); + return snap; + } + + public void pre(){ + if(keepCursorInView){ + boolean horzScroll = false; + float max_ptx = caretX - tw + 2; + if(endTLHI != null){ + if(ptx > caretX){ // Scroll to the left (text moves right) + ptx -= HORZ_SCROLL_RATE; + if(ptx < 0) ptx = 0; + horzScroll = true; + } + else if(ptx < max_ptx){ // Scroll to the right (text moves left)? + ptx += HORZ_SCROLL_RATE; + if(ptx > max_ptx) ptx = max_ptx; + horzScroll = true; + } + // Ensure that we show as much text as possible keeping the caret in view + // This is particularly important when deleting from the end of the text + if(ptx > 0 && endTLHI.tli.layout.getAdvance() - ptx < tw - 2){ + ptx = Math.max(0, endTLHI.tli.layout.getAdvance() - tw - 2); + horzScroll = true; + } + if(horzScroll && hsb != null) + hsb.setValue(ptx / (stext.getMaxLineLength() + 4)); + } + // If we have scrolled invalidate the buffer otherwise forget it + if(horzScroll) + bufferInvalid = true; + else + keepCursorInView = false; + } + } + + public void mouseEvent(MouseEvent event){ + if(!visible || !enabled || !available) return; + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + ox -= tx; oy -= ty; // Remove translation + + currSpot = whichHotSpot(ox, oy); + + if(currSpot == 1 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(currSpot == 1){ + if(focusIsWith != this && z >= focusObjectZ()){ + keepCursorInView = true; + takeFocus(); + } + dragging = false; + if(stext == null || stext.length() == 0){ + stext = new StyledString(" ", wrapWidth); + stext.getLines(buffer.g2); + } + endTLHI = stext.calculateFromXY(buffer.g2, ox + ptx, oy + pty); + startTLHI = new TextLayoutHitInfo(endTLHI); + calculateCaretPos(endTLHI); + bufferInvalid = true; + } + else { // Not over this control so if we have focus loose it + if(focusIsWith == this) + loseFocus(null); + } + break; + case MouseEvent.RELEASE: + dragging = false; + bufferInvalid = true; + break; + case MouseEvent.DRAG: + if(focusIsWith == this){ + keepCursorInView = true; + dragging = true; + endTLHI = stext.calculateFromXY(buffer.g2, ox + ptx, oy + pty); + calculateCaretPos(endTLHI); + fireEvent(this, GEvent.SELECTION_CHANGED); + bufferInvalid = true; + } + break; + } + } + + protected void keyPressedProcess(int keyCode, char keyChar, boolean shiftDown, boolean ctrlDown){ + boolean validKeyCombo = true; + + switch(keyCode){ + case LEFT: + moveCaretLeft(endTLHI); + break; + case RIGHT: + moveCaretRight(endTLHI); + break; + case GConstants.HOME: + moveCaretStartOfLine(endTLHI); + break; + case GConstants.END: + moveCaretEndOfLine(endTLHI); + break; + case 'A': + if(ctrlDown){ + moveCaretStartOfLine(startTLHI); + moveCaretEndOfLine(endTLHI); + // Make shift down so that the start caret position is not + // moved to match end caret position. + shiftDown = true; + } + break; + case 'C': + if(ctrlDown) + GClip.copy(getSelectedText()); + validKeyCombo = false; + break; + case 'V': + if(ctrlDown){ + String p = GClip.paste(); + p.replaceAll("\n", ""); + if(p.length() > 0){ + // delete selection and add + if(hasSelection()) + stext.deleteCharacters(pos, nbr); + stext.insertCharacters(pos, p); + adjust = p.length(); + textChanged = true; + } + } + break; + default: + validKeyCombo = false; + } + calculateCaretPos(endTLHI); + + if(validKeyCombo){ + if(!shiftDown) // Not extending selection + startTLHI.copyFrom(endTLHI); + bufferInvalid = true; // Selection changed + } + } + + protected void keyTypedProcess(int keyCode, char keyChar, boolean shiftDown, boolean ctrlDown){ + int ascii = (int)keyChar; + + if(ascii >= 32 && ascii < 127){ + if(hasSelection()) + stext.deleteCharacters(pos, nbr); + stext.insertCharacters(pos, "" + keyChar); + adjust = 1; textChanged = true; + } + else if(keyChar == BACKSPACE){ + if(hasSelection()){ + stext.deleteCharacters(pos, nbr); + adjust = 0; textChanged = true; + } + else if(stext.deleteCharacters(pos - 1, 1)){ + adjust = -1; textChanged = true; + } + } + else if(keyChar == DELETE){ + if(hasSelection()){ + stext.deleteCharacters(pos, nbr); + adjust = 0; textChanged = true; + } + else if(stext.deleteCharacters(pos, 1)){ + adjust = 0; textChanged = true; + } + } + else if(keyChar == ENTER || keyChar == RETURN) { + fireEvent(this, GEvent.ENTERED); + // If we have a tab manager and can tab forward then do so + if(tabManager != null && tabManager.nextControl(this)){ + startTLHI.copyFrom(endTLHI); + return; + } + } + else if(keyChar == TAB){ + // If possible move to next text control + if(tabManager != null){ + boolean result = (shiftDown) ? tabManager.prevControl(this) : tabManager.nextControl(this); + if(result){ + startTLHI.copyFrom(endTLHI); + return; + } + } + } + // If we have emptied the text then recreate a one character string (space) + if(stext.length() == 0){ + stext.insertCharacters(0, " "); + adjust++; textChanged = true; + } + } + + public void draw(){ + if(!visible) return; + updateBuffer(); + + winApp.pushStyle(); + winApp.pushMatrix(); + + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + + winApp.pushMatrix(); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + + // Draw caret if text display area + if(focusIsWith == this && showCaret && endTLHI.tli != null){ + float[] cinfo = endTLHI.tli.layout.getCaretInfo(endTLHI.thi); + float x_left = - ptx + cinfo[0]; + float y_top = - pty + endTLHI.tli.yPosInPara; + float y_bot = y_top - cinfo[3] + cinfo[5]; + if(x_left >= 0 && x_left <= tw && y_top >= 0 && y_bot <= th){ + winApp.strokeWeight(1.9f); + winApp.stroke(palette[15]); + winApp.line(tx+x_left, ty+Math.max(0, y_top), tx+x_left, ty+Math.min(th, y_bot)); + } + } + + winApp.popMatrix(); + + if(children != null){ + for(GAbstractControl c : children) + c.draw(); + } + winApp.popMatrix(); + winApp.popStyle(); + } + + /** + * If the buffer is invalid then redraw it. + * @TODO need to use palette for colours + */ + protected void updateBuffer(){ + if(bufferInvalid) { + Graphics2D g2d = buffer.g2; + // Get the latest lines of text + LinkedList lines = stext.getLines(g2d); + if(lines.isEmpty() && defaultText != null) + lines = defaultText.getLines(g2d); + + bufferInvalid = false; + TextLayoutHitInfo startSelTLHI = null, endSelTLHI = null; + + buffer.beginDraw(); + // Whole control surface if opaque + if(opaque) + buffer.background(palette[6]); + else + buffer.background(buffer.color(255,0)); + + // Now move to top left corner of text display area + buffer.translate(tx,ty); + + // Typing area surface + buffer.noStroke(); + buffer.fill(palette[7]); + buffer.rect(-1,-1,tw+2,th+2); + + g2d.setClip(gpTextDisplayArea); + buffer.translate(-ptx, -pty); + // Translate in preparation for display selection and text + + if(hasSelection()){ + if(endTLHI.compareTo(startTLHI) == -1){ + startSelTLHI = endTLHI; + endSelTLHI = startTLHI; + } + else { + startSelTLHI = startTLHI; + endSelTLHI = endTLHI; + } + } + // Display selection and text + for(TextLayoutInfo lineInfo : lines){ + TextLayout layout = lineInfo.layout; + buffer.translate(0, layout.getAscent()); + // Draw selection if any + if(hasSelection() && lineInfo.compareTo(startSelTLHI.tli) >= 0 && lineInfo.compareTo(endSelTLHI.tli) <= 0 ){ + int ss = startSelTLHI.thi.getInsertionIndex(); + int ee = endSelTLHI.thi.getInsertionIndex(); + g2d.setColor(jpalette[14]); + Shape selShape = layout.getLogicalHighlightShape(ss, ee); + g2d.fill(selShape); + } + // Draw text + g2d.setColor(jpalette[2]); + lineInfo.layout.draw(g2d, 0, 0); + buffer.translate(0, layout.getDescent() + layout.getLeading()); + } + g2d.setClip(null); + buffer.endDraw(); + } + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextIconAlignBase.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextIconAlignBase.java new file mode 100644 index 0000000..b94e496 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTextIconAlignBase.java @@ -0,0 +1,172 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import processing.core.PApplet; +import processing.core.PImage; + +/** + * Base class for controls with text and/or icon.
+ * + * This class forms the basis for any control that has text and/or an icon.
+ * Use the setIcon, setIconAlign, setText and setTextAlign to control + * horizontal and vertical alignment of the icon and text withing the control face. + * + * @author Peter Lager + * + */ +public abstract class GTextIconAlignBase extends GTextAlign { + + protected PImage[] bicon = null; + protected int iconW = 0, iconH = 0; + protected GAlign iconAlignH = GAlign.RIGHT, iconAlignV = GAlign.MIDDLE; + protected int siX, siY; + + + public GTextIconAlignBase(PApplet theApplet, float p0, float p1, float p2, float p3) { + super(theApplet, p0, p1, p2, p3); + } + + /** + * Set the text to be displayed and calculate the wrap length taking into + * account any icon set. + * + * @param text + */ + public void setText(String text){ + if(text == null || text.length() == 0 ) + text = " "; + if(iconW == 0) + stext = new StyledString(text, (int) width - TPAD2); + else + stext = new StyledString(text, (int) width - iconW - TPAD4); + bufferInvalid = true; + } + + /** + * Set the icon to be used and the horizontal and/or vertical icon alignment. + * Use the constants in GAlign e.g.
GAlign.LEFT

+ * + * @param fname the filename of the icon + * @param nbrImages number of tiled images in the icon + * @param horz LEFT or RIGHT + * @param vert TOP, MIDDLE, BOTTOM + */ + public void setIcon(String fname, int nbrImages, GAlign horz, GAlign vert){ + PImage iconImage = ImageManager.loadImage(winApp, fname); + setIcon(iconImage, nbrImages, horz, vert); + } + + /** + * Set the icon to be used and the horizontal and/or vertical icon alignment. + * Use the constants in GAlign e.g.
GAlign.LEFT

+ * + * If you want to set just one of these then pass null in the other + * + * @param icon the icon + * @param nbrImages number of tiled images in the icon + * @param horz LEFT or RIGHT + * @param vert TOP, MIDDLE, BOTTOM + */ + public void setIcon(PImage icon, int nbrImages, GAlign horz, GAlign vert){ + if(icon != null){ + if(nbrImages == 3) + bicon = ImageManager.makeTiles1D(winApp, icon, nbrImages, 1); + else { + bicon = new PImage[3]; + PImage[] temp = ImageManager.makeTiles1D(winApp, icon, nbrImages, 1); + System.arraycopy(temp, 0, bicon, 0, temp.length); + for(int i = temp.length; i < 3; i++){ + bicon[i] = bicon[i-1]; + } + } + + // We have loaded the image so validate alignment + if(horz != null && horz.isHorzAlign() && horz != GAlign.CENTER){ + iconAlignH = horz; + } + if(vert != null && vert.isVertAlign()){ + iconAlignV = vert; + } + iconW = bicon[0].width; + iconH = bicon[0].height; + stext.setWrapWidth((int) width - iconW - TPAD4); + bufferInvalid = true; + } + } + + /** + * Change the alignment of an existing icon. + * @param horz horizontal alignment (see @see GAlign) + * @param vert vertical alignment (see @see GAlign) + */ + public void setIconAlign(GAlign horz, GAlign vert){ + if(iconW != 0){ + if(horz != null && horz.isHorzAlign() && horz != GAlign.CENTER){ + iconAlignH = horz; + } + if(vert != null && vert != null && vert.isVertAlign()){ + iconAlignV = vert; + } + bufferInvalid = true; + } + } + + /** + * Calculate various values based on alignment of text and icon + */ + protected void calcAlignment(){ + super.calcAlignment(); // calculate the text alignment + if(iconW != 0){ + switch(iconAlignH){ + case LEFT: + siX = TPAD; + if(textAlignH != GAlign.RIGHT) + stX += (iconW + TPAD2); // Image on left so adjust text start x position + break; + case RIGHT: + default: + siX = (int)width - iconW - TPAD2; + if(textAlignH == GAlign.RIGHT) + stX -= (iconW + TPAD2); + break; + } + switch(iconAlignV){ + case TOP: + siY = TPAD; + break; + case BOTTOM: + siY =(int) height - iconH - TPAD2; + break; + case MIDDLE: + default: + siY = (int)(height - iconH)/2; + } + } + } + + public String toString(){ + return tag; + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTimer.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTimer.java new file mode 100644 index 0000000..a80122b --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GTimer.java @@ -0,0 +1,263 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.Method; + +import javax.swing.Timer; + +import processing.core.PApplet; + +/** + * This class is used to trigger events at user defined intervals. The event will + * call a user defined method/function. The only restriction is that the method + * used has a single parameter of type GTimer and returns void eg
+ *
+ * void fireBall(GTimer timer){ ... }
+ * 

+ * + * Each timer object must have its own handler + * + * It has no visible GUI representation so will not appear in the GUI. + * + * @author Peter Lager + * + */ +public class GTimer implements GConstantsInternal { + + /* This must be set by the constructor */ + protected PApplet app; + + /* The object to handle the event */ + protected Object eventHandlerObject = null; + /* The method in eventHandlerObject to execute */ + protected Method eventHandlerMethod = null; + /* the name of the method to handle the event */ + protected String eventHandlerMethodName; + + // The number of repeats i.e. events to be fired. + protected int nrepeats = -1; + + protected Timer timer = null; + + /** + * Create the GTimer object with this ctor. + * + * 'methodName' is the method/function to be called every 'interval' + * milliseconds. 'obj' is the object that contains the method/function + * 'methodName' + * + * For most users 'methodName' will be in their main sketch so this + * parameter has the same value as 'theApplet' + * + * @param theApplet a reference to the PApplet object (invariably this) + * @param obj the object that has the method to be executed (likely to be this) + * @param methodName the name of the method to be called by the timer + * @param delay the initial delay and the time (in millisecs) between function calls + */ + public GTimer(PApplet theApplet, Object obj, String methodName, int delay){ + app = theApplet; + createEventHandler(obj, methodName); + // If we have something to handle the event then create the Timer + if(eventHandlerObject != null){ + timer = new Timer(delay, new ActionListener(){ + + public void actionPerformed(ActionEvent e) { + fireEvent(); + } + + }); + timer.setInitialDelay(delay); + timer.setDelay(delay); + timer.stop(); + } + } + + public GTimer(PApplet theApplet, Object obj, String methodName, int delay, int initDelay){ + app = theApplet; + createEventHandler(obj, methodName); + // If we have something to handle the event then create the Timer + if(eventHandlerObject != null){ + timer = new Timer(delay, new ActionListener(){ + + public void actionPerformed(ActionEvent e) { + fireEvent(); + } + + }); + timer.setInitialDelay(initDelay); + timer.setDelay(delay); + timer.stop(); + } + } + + /** + * See if 'obj' has a parameterless method called 'methodName' and + * if so keep a reference to it. + * + * @param obj + * @param methodName + */ + protected void createEventHandler(Object handlerObj, String methodName){ + try{ + eventHandlerMethod = handlerObj.getClass().getMethod(methodName, new Class[] { GTimer.class } ); + eventHandlerObject = handlerObj; + eventHandlerMethodName = methodName; + } catch (Exception e) { + GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class[] { GTimer.class }}); + eventHandlerObject = null; + } + } + + /** + * Attempt to fire an event for this timer. This will call the + * method/function defined in the ctor. + */ + protected void fireEvent(){ + if(eventHandlerMethod != null){ + try { + eventHandlerMethod.invoke(eventHandlerObject, this); + if(--nrepeats == 0) + stop(); + } catch (Exception e) { + GMessenger.message(EXCP_IN_HANDLER, + new Object[] {eventHandlerObject, eventHandlerMethodName, e } ); + System.out.println("Disabling " + eventHandlerMethod.getName() + " due to an unknown error"); + eventHandlerMethod = null; + eventHandlerObject = null; + } + } + } + + /** + * Start the timer (call the method forever) + */ + public void start(){ + this.nrepeats = -1; + if(timer != null) + timer.start(); + } + + /** + * Start the timer and call the method for the number of + * times indicated by nrepeats + * If nrepeats is <=0 then repeat forever + * + * @param nrepeats + */ + public void start(int nrepeats){ + this.nrepeats = nrepeats; + if(timer != null) + timer.start(); + } + + /** + * Stop the timer (can be restarted with start() method) + */ + public void stop(){ + if(timer != null) + timer.stop(); + } + + /** + * Is the timer running? + * @return true if running + */ + public boolean isRunning(){ + if(timer != null) + return timer.isRunning(); + else + return false; + } + + /** + * Set the interval between events + * @param interval delay between events in milliseconds + */ + public void setInterval(int interval){ + if(timer != null) + timer.setDelay(interval); + } + + /** + * Set the delay before the first event is triggered + * @param initDelay initial delay in milliseconds + */ + public void setInitialDelay(int initDelay){ + if(timer != null) + timer.setInitialDelay(initDelay); + } + + /** + * Sets the initial delay and the interval between events.
+ * This is equivalent to calling both -
+ *
+	 * setInterval(delay);
+	 * setInitialDelay(delay);
+	 * 

+ * @param delay initial delay and interval in milliseconds + */ + public void setDelay(int delay){ + if(timer != null){ + timer.setInitialDelay(delay); + timer.setDelay(delay); + } + } + + /** + * Get the interval time (milliseconds) between + * events. + * @return interval in millsecs or -1 if the timer failed to + * be created. + */ + public int getInterval(){ + if(timer != null) + return timer.getDelay(); + else + return -1; + } + + /** + * Get the initial delay time (milliseconds). + * @return initial delay in millsecs or -1 if the timer failed to + * be created. + */ + public int getInitialDelay(){ + if(timer != null) + return timer.getInitialDelay(); + else + return -1; + } + + /** + * See if the GTimer object has been created successfully + * @return true if successful + */ + public boolean isValid(){ + return (eventHandlerObject != null && timer != null); + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GToggleControl.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GToggleControl.java new file mode 100644 index 0000000..4d26fa9 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GToggleControl.java @@ -0,0 +1,236 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2012 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import g4p_controls.HotSpot.HSrect; +import g4p_controls.StyledString.TextLayoutInfo; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.TextLayout; +import java.util.LinkedList; + +import processing.core.PApplet; +import processing.core.PGraphicsJava2D; +import processing.event.MouseEvent; + +/** + * This class forms the basis for any two-state type control (toggle switch).
+ * A toggle control can be in one of 2 states selected or not selected + * and is the base class for the GOption and GCheckbox controls. + * + * @author Peter Lager + * + */ +public abstract class GToggleControl extends GTextIconAlignBase { + + protected GToggleGroup group = null; + + protected boolean selected = false; + + public GToggleControl(PApplet theApplet, float p0, float p1, float p2, float p3) { + super(theApplet, p0, p1, p2, p3); + // The image buffer is just for the typing area + buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D); + buffer.rectMode(PApplet.CORNER); + buffer.g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + opaque = false; + hotspots = new HotSpot[]{ + new HSrect(1, 0, 0, width, height) // control surface + }; + } + + // This method is called if this control is added to a toggle group. A toggle group + // enforces single option selection from the group. Override this with an empty method + // to allow each toggle control to be independant of others. + protected void setToggleGroup(GToggleGroup tg) { + this.group = tg; + } + + /** + * Get the toggle group. If null is returned then it is not part + * of the group. + */ + public GToggleGroup getToggleGroup(){ + return group; + } + + /** + * @return the selected + */ + public boolean isSelected() { + return selected; + } + + /** + * @param selected the selected to set + */ + public void setSelected(boolean selected) { + if(this.selected != selected) + bufferInvalid = true; + if(selected && group != null) + group.makeSelected(this); + this.selected = selected; + } + +// public void setSelected() { +// setSelected(true); +// } + + /* + * Only executed when clicked in the GUI. + */ + protected void hasBeenClicked(){ + if(group == null){ + // Independent action e.g. check box + selected = !selected; + bufferInvalid = true; + } + else { + // Only need to do something if we click on an unselected option + if(!selected) + setSelected(true); + } + } + + public void mouseEvent(MouseEvent event){ + // If this option does not belong to a group then ignore mouseEvents + if(!visible || !enabled || !available) return; + + + calcTransformedOrigin(winApp.mouseX, winApp.mouseY); + currSpot = whichHotSpot(ox, oy); + // currSpot == 1 for text display area + if(currSpot >= 0 || focusIsWith == this) + cursorIsOver = this; + else if(cursorIsOver == this) + cursorIsOver = null; + + + switch(event.getAction()){ + case MouseEvent.PRESS: + if(focusIsWith != this && currSpot >= 0 && z > focusObjectZ()){ + dragging = false; + takeFocus(); + } + break; + case MouseEvent.CLICK: + if(focusIsWith == this){ + hasBeenClicked(); + loseFocus(null); + if(selected) + fireEvent(this, GEvent.SELECTED); + else if(group == null) + fireEvent(this, GEvent.DESELECTED); + } + break; + case MouseEvent.DRAG: + dragging = true; + break; + case MouseEvent.RELEASE: + // Release focus without firing an event - that would have + // been done + if(focusIsWith == this && dragging) + this.loseFocus(null); + dragging = false; + break; + } + } + + public void draw(){ + if(!visible) return; + + // Update buffer if invalid + updateBuffer(); + winApp.pushStyle(); + + winApp.pushMatrix(); + // Perform the rotation + winApp.translate(cx, cy); + winApp.rotate(rotAngle); + // Move matrix to line up with top-left corner + winApp.translate(-halfWidth, -halfHeight); + // Draw buffer + winApp.imageMode(PApplet.CORNER); + if(alphaLevel < 255) + winApp.tint(TINT_FOR_ALPHA, alphaLevel); + winApp.image(buffer, 0, 0); + winApp.popMatrix(); + + winApp.popStyle(); + } + + protected void updateBuffer(){ + if(bufferInvalid) { + Graphics2D g2d = buffer.g2; + // Get the latest lines of text + LinkedList lines = stext.getLines(g2d); + bufferInvalid = false; + + buffer.beginDraw(); + // Back ground colour + if(opaque == true) + buffer.background(palette[6]); + else + buffer.background(buffer.color(255,0)); + // Calculate text and icon placement + calcAlignment(); + // If there is an icon draw it + if(iconW != 0) + if(selected) + buffer.image(bicon[1], siX, siY); + else + buffer.image(bicon[0], siX, siY); + float wrapWidth = stext.getWrapWidth(); + float sx = 0, tw = 0; + buffer.translate(stX, stY); + for(TextLayoutInfo lineInfo : lines){ + TextLayout layout = lineInfo.layout; + buffer.translate(0, layout.getAscent()); + switch(textAlignH){ + case CENTER: + tw = layout.getAdvance(); + tw = (tw > wrapWidth) ? tw - wrapWidth : tw; + sx = (wrapWidth - tw)/2; + break; + case RIGHT: + tw = layout.getAdvance(); + tw = (tw > wrapWidth) ? tw - wrapWidth : tw; + sx = wrapWidth - tw; + break; + case LEFT: + case JUSTIFY: + default: + sx = 0; + } + // display text + g2d.setColor(jpalette[2]); + lineInfo.layout.draw(g2d, sx, 0); + buffer.translate(0, layout.getDescent() + layout.getLeading()); + } + buffer.endDraw(); + } + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GToggleGroup.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GToggleGroup.java new file mode 100644 index 0000000..0f5900f --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GToggleGroup.java @@ -0,0 +1,74 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + + +/** + * Use this class to create a single selection collection of options.
+ * + * To use create an object of this class and then add GOption objects to it with + * the addControl method.
+ * + * You cannot add GCheckbox objects because they are designed to work independently. + * + * @author Peter Lager + * + */ +public class GToggleGroup { + + private GToggleControl selected = null; + private GToggleControl deselected = null; + + /** + * Create a toggle group object. + */ + public GToggleGroup(){ } + + /** + * Add a GOption object to this group. + * @param tc + */ + public void addControl(GToggleControl tc){ + tc.setToggleGroup(this); + } + + /** + * Add a set of comma separated GOptions. + * @param controls + */ + public void addControls(GToggleControl... controls ){ + for(GToggleControl tc : controls) + tc.setToggleGroup(this); + } + + /** + * Used internally to change selection + */ + void makeSelected(GToggleControl tc){ + deselected = selected; + if(deselected != null) + deselected.setSelected(false); + selected = tc; + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GValueControl.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GValueControl.java new file mode 100644 index 0000000..6295940 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GValueControl.java @@ -0,0 +1,512 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import processing.core.PApplet; + +/** + * Base class for all slider and knob type controls. + * + * This class enables the creation of tick marks and constraining values to + * the tick mark values.
+ * + * It also controls how the values are to be displayed INTEGER, DECIMAL or EXPONENT + * + * @author Peter Lager + * + */ +public abstract class GValueControl extends GAbstractControl { + + protected StyledString ssStartLimit, ssEndLimit, ssValue; + + protected float startLimit = 0, endLimit = 1; + protected boolean showLimits = false; + + protected int valueType = DECIMAL; + protected int precision = 2; + protected String unit = ""; + protected boolean showValue = false; + + + protected float parametricPos = 0.5f, parametricTarget = 0.5f; + protected float easing = 1.0f; // must be >= 1.0 + + + protected int nbrTicks = 2; + protected boolean stickToTicks = false; + protected boolean showTicks = false; + + protected boolean limitsInvalid = true; + + // Offset to between mouse and thumb centre + protected float offset; + + public GValueControl(PApplet theApplet, float p0, float p1, float p2, float p3) { + super(theApplet, p0, p1, p2, p3); + } + + public void pre(){ + if(Math.abs(parametricTarget - parametricPos) > epsilon){ + parametricPos += (parametricTarget - parametricPos) / easing; + updateDueToValueChanging(); + bufferInvalid = true; + if(Math.abs(parametricTarget - parametricPos) > epsilon){ + fireEvent(this, GEvent.VALUE_CHANGING); + } + else { + parametricPos = parametricTarget; + fireEvent(this, GEvent.VALUE_STEADY); + } + } + } + + /** + * This should be overridden in child classes so they can perform any class specific + * actions when the value changes. + * Override this in GSlider to change the hotshot poaition. + */ + protected void updateDueToValueChanging(){ + } + + /** + * Used to format the number into a string for display. + * @param number + * @return a string representing the number + */ + protected String getNumericDisplayString(float number){ + String s = ""; + switch(valueType){ + case INTEGER: + s = String.format("%d %s", Math.round(number), unit); + break; + case DECIMAL: + s = String.format("%." + precision + "f %s", number, unit); + break; + case EXPONENT: + s = String.format("%." + precision + "e %s", number, unit); + break; + } + return s.trim(); + } + + + /** + * Sets the range of values to be returned. This method will + * assume that you want to set the valueType to INTEGER + * + * @param start the start value of the range + * @param end the end value of the range + */ + public void setLimits(int start, int end){ + startLimit = start; + endLimit = end; + setEpsilon(); + valueType = INTEGER; + limitsInvalid = true; + bufferInvalid = true; + } + + /** + * Sets the initial value and the range of values to be returned. This + * method will assume that you want to set the valueType to INTEGER. + * + * @param initValue the initial value + * @param start the start value of the range + * @param end the end value of the range + */ + public void setLimits(int initValue, int start, int end){ + startLimit = start; + endLimit = end; + valueType = INTEGER; + setEpsilon(); + limitsInvalid = true; + bufferInvalid = true; + setValue(initValue); + parametricPos = parametricTarget; + updateDueToValueChanging(); + } + + /** + * Sets the range of values to be returned. This method will + * assume that you want to set the valueType to DECIMAL + * + * @param start + * @param end + */ + public void setLimits(float start, float end){ + startLimit = start; + endLimit = end; + if(valueType == INTEGER){ + valueType = DECIMAL; + setPrecision(1); + } + setEpsilon(); + limitsInvalid = true; + bufferInvalid = true; + } + + /** + * Sets the initial value and the range of values to be returned. This + * method will assume that you want to set the valueType to DECIMAL. + * + * @param initValue the initial value + * @param start the start value of the range + * @param end the end value of the range + */ + public void setLimits(float initValue, float start, float end){ + startLimit = start; + endLimit = end; + initValue = PApplet.constrain(initValue, start, end); + if(valueType == INTEGER){ + valueType = DECIMAL; + setPrecision(1); + } + setEpsilon(); + limitsInvalid = true; + bufferInvalid = true; + setValue(initValue); + parametricPos = parametricTarget; + updateDueToValueChanging(); + } + + /** + * Set the value for the slider.
+ * The user must ensure that the value is valid for the slider range. + * @param v + */ + public void setValue(float v){ + if(valueType == INTEGER) + v = Math.round(v); + float t = (v - startLimit) / (endLimit - startLimit); + t = PApplet.constrain(t, 0.0f, 1.0f); + if(stickToTicks) + t = findNearestTickValueTo(t); + parametricTarget = t; + } + + /** + * For DECIMAL values this sets the number of decimal places to + * be displayed. + * @param nd must be >= 1 otherwise will use 1 + */ + public void setPrecision(int nd){ + nd = PApplet.constrain(nd, 1, 5); + if(nd < 1) + nd = 1; + if(nd != precision){ + precision = nd; + setEpsilon(); + limitsInvalid = true; + bufferInvalid = true; + } + } + + /** + * Make epsilon to match the value of 1 pixel or the precision which ever is the smaller + */ + protected void setEpsilon(){ + epsilon = (float) Math.min(0.001, Math.pow(10, -precision)); + } + + /** + * The units to be displayed with the current and limit values e.g. + * kg, m, ($), fps etc.
+ * Do not use long labels such as 'miles per hour' as these take a + * lot of space and can look messy. + * + * @param units for example kg, m, ($), fps + */ + public void setUnits(String units){ + if(units == null) + units = ""; + if(!unit.equals(units)){ + unit = units; + limitsInvalid = true; + bufferInvalid = true; + } + } + + /** + * Set the numberFormat, precision and units in one go.
+ * Valid number formats are INTEGER, DECIMAL, EXPONENT
+ * Precision must be >= 1 and is ignored for INTEGER. + * + * @param numberFormat INTEGER, DECIMAL or EXPONENT + * @param precision must be >= 1 + * @param unit for example kg, m, ($), fps + */ + public void setNumberFormat(int numberFormat, int precision, String unit){ + this.unit = (unit == null) ? "" : unit; + setNumberFormat(numberFormat, precision); + } + + /** + * Set the numberFormat and precision in one go.
+ * Valid number formats are INTEGER, DECIMAL, EXPONENT
+ * Precision must be >= 1 and is ignored for INTEGER. + * + * @param numberFormat G4P.INTEGER, G4P.DECIMAL orG4P. EXPONENT + * @param precision must be >= 1 + */ + public void setNumberFormat(int numberFormat, int precision){ + switch(numberFormat){ + case INTEGER: + case DECIMAL: + case EXPONENT: + this.valueType = numberFormat; + break; + default: + valueType = DECIMAL; + } + setPrecision(precision); + bufferInvalid = true; + } + + /** + * Set the numberFormat and precision in one go.
+ * Valid number formats are INTEGER, DECIMAL, EXPONENT
+ * Precision must be >= 1 and is ignored for INTEGER. + * + * @param numberFormat G4P.INTEGER, G4P.DECIMAL or G4P.EXPONENT + */ + public void setNumberFormat(int numberFormat){ + switch(numberFormat){ + case INTEGER: + case DECIMAL: + case EXPONENT: + this.valueType = numberFormat; + break; + default: + valueType = DECIMAL; + } + bufferInvalid = true; + } + + /** + * Get the current value as a float + */ + public float getValueF(){ + return startLimit + (endLimit - startLimit) * parametricPos; + } + + /** + * Get the current value as an integer.
+ * DECIMAL and EXPONENT value types will be rounded to the nearest integer. + */ + public int getValueI(){ + return Math.round(startLimit + (endLimit - startLimit) * parametricPos); + } + + /** + * If we are using labels then this will get the label text + * associated with the current value.
+ * If labels have not been set then return null + */ + public String getValueS(){ + return getNumericDisplayString(getValueF()); + } + + /** + * Get the current value used for easing. + * @return the easing + */ + public float getEasing() { + return easing; + } + + /** + * Set the amount of easing to be used when a value is changing. The default value + * is 1 (no easing) values > 1 will cause the value to rush from its starting value + * and decelerate towards its final values. In other words it smoothes the movement + * of the slider thumb or knob rotation. + * + * @param easeBy the easing to set + */ + public void setEasing(float easeBy) { + easing = (easeBy < 1) ? 1 : easeBy; + } + + /** + * Get the number of tick marks. + * @return the nbrTicks + */ + public int getNbrTicks() { + return nbrTicks; + } + + /** + * The number of ticks must be >= 2 since 2 are required for the slider limits. + * + * @param noOfTicks the nbrTicks to set + */ + public void setNbrTicks(int noOfTicks) { + if(noOfTicks < 2) + noOfTicks = 2; + if(nbrTicks != noOfTicks){ + nbrTicks = noOfTicks; + bufferInvalid = true; + if(stickToTicks) + parametricTarget = findNearestTickValueTo(parametricPos); + } + } + + /** + * Is the value constrained to the tick marks? + * @return the stickToTicks true if values constrained else false + */ + public boolean isStickToTicks() { + return stickToTicks; + } + + /** + * Specify whether the values are to be constrained to the tick marks or not. + * It will automatically display tick marks if set true. + * @param stickToTicks true if you want to constrain the values else false + */ + public void setStickToTicks(boolean stickToTicks) { + this.stickToTicks = stickToTicks; + if(stickToTicks){ + setShowTicks(true); + parametricTarget = findNearestTickValueTo(parametricPos); + bufferInvalid = true; + } + } + + /** + * These are normalised values i.e. between 0.0 and 1.0 inclusive + * @param p + * @return the parametric value of the nearest tick + */ + protected float findNearestTickValueTo(float p){ + float tickSpace = 1.0f / (nbrTicks - 1); + int tn = (int) (p / tickSpace + 0.5f); + return tickSpace * tn; + } + + /** + * Are the tick marks visible? + * @return the showTicks + */ + public boolean isShowTicks() { + return showTicks; + } + + /** + * Set whether the tick marks are to be displayed or not. + * @param showTicks the showTicks to set + */ + public void setShowTicks(boolean showTicks) { + if(this.showTicks != showTicks){ + this.showTicks = showTicks; + bufferInvalid = true; + } + } + + /** + * Are the limit values visible? + * @return the showLimits + */ + public boolean isShowLimits() { + return showLimits; + } + + /** + * Set whether the limits are to be displayed or not. + * @param showLimits the showLimits to set + */ + public void setShowLimits(boolean showLimits) { + this.showLimits = showLimits; + bufferInvalid = true; + } + + /** + * Is the current value to be displayed? + * @return the showValue + */ + public boolean isShowValue() { + return showValue; + } + + /** + * Set whether the current value is to be displayed or not. + * @param showValue the showValue to set + */ + public void setShowValue(boolean showValue) { + this.showValue = showValue; + bufferInvalid = true; + } + + /** + * Convenience method to set what is to be drawn to the screen. + * @param opaque show background + * @param ticks show tick marks + * @param value show current value + * @param limits show min and max values (limits) + */ + public void setShowDecor(boolean opaque, boolean ticks, boolean value, boolean limits){ + setShowValue(value); + bufferInvalid = true; + setOpaque(opaque); + setShowTicks(ticks); + setShowLimits(limits); + } + + /** + * @return the startLimit + */ + public float getStartLimit() { + return startLimit; + } + + /** + * @return the endLimit + */ + public float getEndLimit() { + return endLimit; + } + + /** + * + * @return the valueType + */ + public int getValueType() { + return valueType; + } + + /** + * Precision used with floating point numbers + * @return the precision + */ + public int getPrecision() { + return precision; + } + + /** + * @return the unit + */ + public String getUnit() { + return unit; + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GValueControl2D.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GValueControl2D.java new file mode 100644 index 0000000..5e9ac20 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GValueControl2D.java @@ -0,0 +1,87 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2013 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import processing.core.PApplet; + +/** + * Base class for controls that have 2 variables e.f. GSlider2D + * + * @author Peter Lager + * + */ +public abstract class GValueControl2D extends GAbstractControl { + + static protected int THUMB_SPOT = 1; + static protected int TRACK_SPOT = 2; + + + protected float parametricPosX = 0.5f, parametricTargetX = 0.5f; + protected float parametricPosY = 0.5f, parametricTargetY = 0.5f; + + protected float easing = 1.0f; // must be >= 1.0 + + // Offset to between mouse and thumb centre + protected float offsetH, offsetV; + + protected int valueType = DECIMAL; + protected int precision = 2; + + public GValueControl2D(PApplet theApplet, float p0, float p1, float p2, float p3) { + super(theApplet, p0, p1, p2, p3); + } + + public void pre(){ + if(Math.abs(parametricTargetX - parametricPosX) > epsilon || Math.abs(parametricTargetY - parametricPosY) > epsilon){ + parametricPosX += (parametricTargetX - parametricPosX) / easing; + parametricPosY += (parametricTargetY - parametricPosY) / easing; + updateDueToValueChanging(); + bufferInvalid = true; + if(Math.abs(parametricTargetX - parametricPosX) > epsilon || Math.abs(parametricTargetY - parametricPosY) > epsilon){ + fireEvent(this, GEvent.VALUE_CHANGING); + } + else { + parametricPosX = parametricTargetX; + parametricPosY = parametricTargetY; + fireEvent(this, GEvent.VALUE_STEADY); + } + } + } + + /** + * This should be overridden in child classes so they can perform any class specific + * actions when the value changes. + * Override this in GSlider to change the hotshot poaition. + */ + protected void updateDueToValueChanging(){ + } + + /** + * Make epsilon to match the value of 1 pixel or the precision which ever is the smaller + */ + protected void setEpsilon(){ + epsilon = (float) Math.min(0.001, Math.pow(10, -precision)); + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWinApplet.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWinApplet.java new file mode 100644 index 0000000..36bf262 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWinApplet.java @@ -0,0 +1,169 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import processing.core.PApplet; +import processing.core.PImage; +import processing.event.KeyEvent; +import processing.event.MouseEvent; + +/** + * CLASS FOR INTERNAL USE ONLY
+ * + * This class extends PApplet and provides a drawing surface for + * the GWindow class. Do not instantiate objects of this class, create + * GWindow objects instead. + * + * The event handling methods here are NOT called by Processing directly. Instead events are caught by the + * GWindowInfo class which then call the appropriate method this class.
+ * These methods use reflection to execute methods registered by the GWindo object that owns this class. + * + * @author Peter Lager + */ +@SuppressWarnings("serial") +public class GWinApplet extends PApplet implements GConstants, GConstantsInternal { + + // Must be set by GWindow 'owning' this PApplet + public GWindow owner; + // The applet width and height + public int appWidth, appHeight; + // applet graphics mode e.g. JAVA2D, P3D etc. + public String mode; + + // background colour + public int bkColor; + + // background image if any + public PImage bkImage = null; + + public boolean autoClear = true; + + public GWinApplet(String mode){ + super(); + this.mode = mode; + } + + /** + * INTERNAL USE ONLY
+ * The PApplet setup method to intialise the drawing surface + */ + public void setup() { + size(appWidth, appHeight, mode); + } + + /** + * INTERNAL USE ONLY
+ * Use the equivalent method in GWindow + * @param col + */ + public void setBackground(int col){ + bkColor = col; + background(col); + } + + /** + * INTERNAL USE ONLY
+ * This will always be active because this class extends PApplet + */ + public void draw() { + if(autoClear){ + if(bkImage != null) + background(bkImage); + else + background(bkColor); + } + if(owner.drawHandlerObject != null){ + try { + owner.drawHandlerMethod.invoke(owner.drawHandlerObject, new Object[] { this, owner.data }); + } catch (Exception e) { + GMessenger.message(EXCP_IN_HANDLER, + new Object[] {owner.drawHandlerObject, owner.drawHandlerMethodName, e} ); + } + } + } + + /** + * INTERNAL USE ONLY
+ * Use addDMouseHandler in GWindow to activate this method + */ + public void mouseEvent(MouseEvent event){ + if(owner.mouseHandlerObject != null){ + try { + owner.mouseHandlerMethod.invoke(owner.mouseHandlerObject, new Object[] { this, owner.data, event }); + } catch (Exception e) { + GMessenger.message(EXCP_IN_HANDLER, + new Object[] {owner.mouseHandlerObject, owner.mouseHandlerMethodName, e} ); + } + } + } + + /** + * INTERNAL USE ONLY
+ * Use addDKeyHandler in GWindow to activate this method + */ + public void keyEvent(KeyEvent event){ + if(owner.keyHandlerObject != null){ + try { + owner.keyHandlerMethod.invoke(owner.keyHandlerObject, new Object[] { this, owner.data, event }); + } catch (Exception e) { + GMessenger.message(EXCP_IN_HANDLER, + new Object[] {owner.keyHandlerObject, owner.keyHandlerMethodName, e} ); + } + } + } + + /** + * INTERNAL USE ONLY
+ * Use addPreHandler in GWindow to activate this method + */ + public void pre(){ + if(owner.preHandlerObject != null){ + try { + owner.preHandlerMethod.invoke(owner.preHandlerObject, + new Object[] { owner.papplet, owner.data }); + } catch (Exception e) { + GMessenger.message(EXCP_IN_HANDLER, + new Object[] {owner.preHandlerObject, owner.preHandlerMethodName, e} ); + } + } + } + + /** + * INTERNAL USE ONLY
+ * Use addPostHandler in GWindow to activate this method + */ + public void post(){ + if(owner.postHandlerObject != null){ + try { + owner.postHandlerMethod.invoke(owner.postHandlerObject, + new Object[] { owner.papplet, owner.data }); + } catch (Exception e) { + GMessenger.message(EXCP_IN_HANDLER, + new Object[] {owner.postHandlerObject, owner.postHandlerMethodName, e} ); + } + } + } + + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWinData.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWinData.java new file mode 100644 index 0000000..5190182 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWinData.java @@ -0,0 +1,39 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +/** + * This class is used to hold the data associated with a GWindow object. + * + * You should extend this class to provide the data to be processed by + * the window + * + * @author Peter Lager + * + */ +public class GWinData { + + public GWindow owner; + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWindow.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWindow.java new file mode 100644 index 0000000..35ff82e --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWindow.java @@ -0,0 +1,490 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-12 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.lang.reflect.Method; + +import processing.core.PApplet; +import processing.core.PImage; +import processing.event.KeyEvent; +import processing.event.MouseEvent; + +/** + * Objects of this class are separate windows which can be used to hold + * G4P GUI components or used for drawing or both combined. + *

+ * A number of examples are included in the library and can be found + * at www.lagers.org.uk + * + * + * @author Peter Lager + * + */ +@SuppressWarnings("serial") +public class GWindow extends Frame implements GConstants, GConstantsInternal { + + protected PApplet app; + + /** + * Gives direct access to the PApplet object inside the frame + * + */ + public GWinApplet papplet; + + protected String winName; + + public GWinData data; + + protected WindowAdapter winAdapt = null; + + protected int actionOnClose = KEEP_OPEN; + + + /** The object to handle the pre event */ + protected Object preHandlerObject = null; + /** The method in preHandlerObject to execute */ + protected Method preHandlerMethod = null; + /** the name of the method to handle the event */ + protected String preHandlerMethodName; + + /** The object to handle the draw event */ + protected Object drawHandlerObject = null; + /** The method in drawHandlerObject to execute */ + protected Method drawHandlerMethod = null; + /** the name of the method to handle the event */ + protected String drawHandlerMethodName; + + /** The object to handle the key event */ + public Object keyHandlerObject = null; + /** The method in keyHandlerObject to execute */ + public Method keyHandlerMethod = null; + /** the name of the method to handle the event */ + protected String keyHandlerMethodName; + + /** The object to handle the mouse event */ + public Object mouseHandlerObject = null; + /** The method in mouseHandlerObject to execute */ + public Method mouseHandlerMethod = null; + /** the name of the method to handle the event */ + protected String mouseHandlerMethodName; + + /** The object to handle the post event */ + protected Object postHandlerObject = null; + /** The method in postHandlerObject to execute */ + protected Method postHandlerMethod = null; + /** the name of the method to handle the event */ + protected String postHandlerMethodName; + + /** + * Create a window that can be used to hold G4P components or used + * for drawing or both together. + * + * @param theApplet + * @param name + * @param x initial position on the screen + * @param y initial position on the screen + * @param w width of the drawing area (the frame will be bigger to accommodate border) + * @param h height of the drawing area (the frame will be bigger to accommodate border and title bar) + * @param noFrame if true then the frame has no border + * @param mode JAVA2D / P2D / P3D / OPENGL + */ + public GWindow(PApplet theApplet, String name, int x, int y, int w, int h, boolean noFrame, String mode) { + super(name); + winName = name; + windowCtorCore(theApplet, x, y, w, h, null, noFrame, mode, name); + } + + /** + * + * @param theApplet + * @param name + * @param x initial position on the screen + * @param y initial position on the screen + * @param image background image (used to size window) + * @param noFrame if true then the frame has no border + * @param mode JAVA2D / OPENGL + */ + public GWindow(PApplet theApplet, String name, int x, int y, PImage image, boolean noFrame, String mode) { + super(name); + windowCtorCore(theApplet, x, y, image.width, image.height, image, noFrame, mode, name); + } + + /** + * Core stuff for GWindows ctor + * + * @param x + * @param y + * @param w + * @param h + * @param noFrame + * @param mode + */ + private void windowCtorCore(PApplet theApplet, int x, int y, int w, int h, PImage image, boolean noFrame, String mode, String name){ + // If this is the first control to be created then theAapplet must be the sketchApplet + if(G4P.sketchApplet == null) + G4P.sketchApplet = theApplet; + app = theApplet; + winName = name; + + if(mode == null || mode.equals("")) + mode = PApplet.JAVA2D; + + papplet = new GWinApplet(mode); + papplet.owner = this; + papplet.frame = this; + // So we can resize the frame to get the sketch canvas size reqd. + papplet.frame.setResizable(true); + // Now set the window width and height + if(image == null){ + papplet.appWidth = w; + papplet.appHeight = h; + } else { + papplet.bkImage = image; + papplet.appWidth = image.width; + papplet.appHeight = image.height; + } + papplet.bkColor = papplet.color(180); + + // Set the papplet size preferences + papplet.resize(papplet.appWidth, papplet.appHeight); + papplet.setPreferredSize(new Dimension(papplet.appWidth, papplet.appHeight)); + papplet.setMinimumSize(new Dimension(papplet.appWidth, papplet.appHeight)); + + // add the PApplet to the Frame + setLayout(new BorderLayout()); + add(papplet, BorderLayout.CENTER); + + // ensures that the animation thread is started and + // that other internal variables are properly set. + papplet.init(); + + // Set the sketch path to the same as the main PApplet object + papplet.sketchPath = theApplet.sketchPath; + + // Pack the window, position it and make visible + setUndecorated(noFrame); + pack(); + setLocation(x,y); + setVisible(true); + + // Make the window always on top + setOnTop(true); + + // Make sure we have some data even if not used + data = new GWinData(); + data.owner = this; + + // Not resizeable if we are using a back image + super.setResizable(image == null); + + // Make sure G4P knows about this window + G4P.addWindow(this); + } + + + /** + * Add an object that holds the data this window needs to use. + * + * Note: the object can be of any class that extends GWinData. + * + * @param data + */ + public void addData(GWinData data){ + this.data = data; + this.data.owner = this; + } + + /** + * Always make this window appear on top of other windows (or not).
+ * This will not work when run from a remote server (ie Applet over the web) + * for security reasons. In this situation a call to this method is ignored + * and a warning is generated. + * + * @param onTop + */ + public void setOnTop(boolean onTop){ + try{ + setAlwaysOnTop(onTop); + } catch (Exception e){ + if(G4P.showMessages) + System.out.println("Warning: setOnTop() method will not work when the sketch is run from a remote location."); + } + } + + /** + * Sets the location of the window.
+ * (Already available from the Frame class - helps visibility + * of method in G4P reference) + */ + public void setLocation(int x, int y){ + super.setLocation(x,y); + } + + /** + * Sets the visibility of the window
+ * (Already available from the Frame class - helps visibility + * of method in G4P reference) + */ + public void setVisible(boolean visible){ + super.setVisible(visible); + } + + /** + * Determines whether the window is resizabale or not.
+ * This cannot be set to true if a background image is used. + */ + public void setResizable(boolean resizable){ + if(resizable == false) + super.setResizable(false); + else { + if(papplet.bkImage == null) + super.setResizable(true); + } + } + + /** + * Set the background image to be used instead of a plain color background
+ * The window will resize to accommodate the image. + * @param image + */ + public void setBackground(PImage image){ + papplet.noLoop(); + papplet.bkImage = null; + super.setResizable(true); + papplet.resize(image.width, image.height); + papplet.bkImage = image; + papplet.appWidth = image.width; + papplet.appHeight = image.height; + papplet.setPreferredSize(new Dimension(papplet.appWidth, papplet.appHeight)); + papplet.setMinimumSize(new Dimension(papplet.appWidth, papplet.appHeight)); + pack(); + super.setResizable(false); + papplet.loop(); + } + + /** + * Set the background color for the window. + * + * @param col + */ + public void setBackground(int col){ + papplet.bkColor = col; + } + + /** + * By default the background() method is called to set the background image/colour + * every frame. You can switch this off by calling this method with a parameter + * value = false. + * @param auto_clear whether to call the background() method or not + */ + public void setAutoClear(boolean auto_clear){ + papplet.autoClear = auto_clear; + } + + /** + * This sets what happens when the users attempts to close the window.
+ * There are 3 possible actions depending on the value passed.
+ * GWindow.KEEP_OPEN - ignore attempt to close window (default action)
+ * GWindow.CLOSE_WINDOW - close this window, if it is the main window it causes the app to exit
+ * GWindow.EXIT_APP - exit the app, this will cause all windows to close.
+ * @param action the required close action + */ + public void setActionOnClose(int action){ + switch(action){ + case KEEP_OPEN: + removeWindowListener(winAdapt); + winAdapt = null; + actionOnClose = action; + break; + case CLOSE_WINDOW: + case EXIT_APP: + if(winAdapt == null){ + winAdapt = new GWindowAdapter(this); + addWindowListener(winAdapt); + } // end if + actionOnClose = action; + break; + } // end switch + } + + /** + * Get the action to be performed when the user attempts to close + * the window. + * @return actionOnClose + */ + public int getActionOnClose(){ + return actionOnClose; + } + + /** + * This method will fire a WindowClosing event to be captured by the + * GWindow$GWindowAdapter object.
+ * There are 3 possible actions depending on the value passed.
+ * GWindow.KEEP_OPEN - ignore attempt to close window (default action)
+ * GWindow.CLOSE_WINDOW - close this window
+ * GWindow.EXIT_APP - exit the app, this will cause all windows to close.
+ */ + public void close(){ + getToolkit().getSystemEventQueue().postEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)); + } + + /** + * This method guarantees that the window is closed by overriding the KEEP_OPEN action-on-close + * and will fire a WindowClosing event to be captured by the GWindow$GWindowAdapter object.
+ * There are 2 possible actions depending on the currently specified action-on-close.
+ * GWindow.KEEP_OPEN - close this window
+ * GWindow.CLOSE_WINDOW - close this window
+ * GWindow.EXIT_APP - exit the app, this will cause all windows to close.
+ */ + public void forceClose(){ + if(actionOnClose == KEEP_OPEN) + setActionOnClose(CLOSE_WINDOW); + getToolkit().getSystemEventQueue().postEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)); + } + + /** + * Attempt to add the 'draw' handler method. + * The default event handler is a method that returns void and has two + * parameters PApplet and GWinData + * + * @param obj the object to handle the event + * @param methodName the method to execute in the object handler class + */ + public void addDrawHandler(Object obj, String methodName){ + try{ + drawHandlerMethod = obj.getClass().getMethod(methodName, new Class[] {GWinApplet.class, GWinData.class } ); + drawHandlerObject = obj; + drawHandlerMethodName = methodName; + } catch (Exception e) { + GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class[] { GWinApplet.class, GWinData.class } } ); + } + } + + /** + * Attempt to add the 'pre' handler method. + * The default event handler is a method that returns void and has two + * parameters GWinApplet and GWinData + * + * @param obj the object to handle the event + * @param methodName the method to execute in the object handler class + */ + public void addPreHandler(Object obj, String methodName){ + try{ + preHandlerMethod = obj.getClass().getMethod(methodName, new Class[] {GWinApplet.class, GWinData.class } ); + preHandlerObject = obj; + preHandlerMethodName = methodName; + } catch (Exception e) { + GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class[] { GWinApplet.class, GWinData.class } } ); + } + } + + /** + * Attempt to add the 'mouse' handler method. + * The default event handler is a method that returns void and has three + * parameters GWinApplet, GWinData and a MouseEvent + * + * @param obj the object to handle the event + * @param methodName the method to execute in the object handler class + */ + public void addMouseHandler(Object obj, String methodName){ + try{ + mouseHandlerMethod = obj.getClass().getMethod(methodName, + new Class[] {GWinApplet.class, GWinData.class, MouseEvent.class } ); + mouseHandlerObject = obj; + mouseHandlerMethodName = methodName; + } catch (Exception e) { + GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class[] { GWinApplet.class, GWinData.class, MouseEvent.class } } ); + } + } + + /** + * Attempt to add the 'key' handler method. + * The default event handler is a method that returns void and has three + * parameters GWinApplet, GWinData and a KeyEvent + * + * @param obj the object to handle the event + * @param methodName the method to execute in the object handler class + */ + public void addKeyHandler(Object obj, String methodName){ + try{ + keyHandlerMethod = obj.getClass().getMethod(methodName, + new Class[] {GWinApplet.class, GWinData.class, KeyEvent.class } ); + keyHandlerObject = obj; + keyHandlerMethodName = methodName; + } catch (Exception e) { + GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class[] { GWinApplet.class, GWinData.class, KeyEvent.class } } ); + } + } + + /** + * Attempt to add the 'post' handler method. + * The default event handler is a method that returns void and has two + * parameters GWinApplet and GWinData + * + * @param obj the object to handle the event + * @param methodName the method to execute in the object handler class + */ + public void addPostHandler(Object obj, String methodName){ + try{ + postHandlerMethod = obj.getClass().getMethod(methodName, + new Class[] {GWinApplet.class, GWinData.class } ); + postHandlerObject = obj; + postHandlerMethodName = methodName; + } catch (Exception e) { + GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class[] { GWinApplet.class, GWinData.class } } ); + } + } + + /** + * Window adapter class that remembers the window it belongs to so + * it can be used to mark it for closure if required. + * + * @author Peter Lager + */ + public class GWindowAdapter extends WindowAdapter { + GWindow window; + + public GWindowAdapter(GWindow window){ + this.window = window; + } + + public void windowClosing(WindowEvent evt) { + switch(actionOnClose){ + case CLOSE_WINDOW: + window.papplet.noLoop(); + G4P.markWindowForClosure(window); + break; + case EXIT_APP: + System.exit(0); + break; + } + } + } + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWindowCloser.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWindowCloser.java new file mode 100644 index 0000000..4fa1934 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWindowCloser.java @@ -0,0 +1,72 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2013 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.util.ArrayList; + +import processing.core.PApplet; + +/** + * This class will be used to safely close windows provided that their actionOnClose + * action is G4P.CLOSE_WINODW.
+ * This is done here, outside the windows normal Processing event loop to avoid + * concurrent access errors.
+ * This class has to be declared public so it can register the post event, but it should + * not be used directly.
+ * To close a window call the GWinodw close() method. + * + * @author Peter Lager + * + */ +public class GWindowCloser { + + + private ArrayList toDisposeOf; + + GWindowCloser() { + toDisposeOf = new ArrayList(); + } + + public void addWindow(GWindow gwindow){ + toDisposeOf.add(gwindow); + } + + public void post(){ + // System.out.println("Window to dispose " + toDisposeOf.size()); + if(!toDisposeOf.isEmpty()){ + for(GWindow gwindow : toDisposeOf){ + PApplet wapp = gwindow.papplet; + GWindowInfo winfo = G4P.windows.get(wapp); + if(winfo != null){ + winfo.dispose(); + G4P.windows.remove(wapp); + gwindow.dispose(); + } + } + toDisposeOf.clear(); + } + } + + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWindowInfo.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWindowInfo.java new file mode 100644 index 0000000..dab5614 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/GWindowInfo.java @@ -0,0 +1,245 @@ +package g4p_controls; + +import java.util.Collections; +import java.util.LinkedList; + +import processing.core.PApplet; +import processing.core.PConstants; +import processing.core.PMatrix; +import processing.core.PMatrix3D; +import processing.event.KeyEvent; +import processing.event.MouseEvent; + +/** + * DO NOT ATTEMPT TO USE THIS CLASS
+ * + * Although this class and many of its methods are declared public this is to make + * them available through Reflection and means that they should only be used inside + * the library code.
+ * + * This class is used to remember information about a particular applet (i.e. window) + * and is responsible for forwarding events passed to it from Processing.
+ * + * It remembers the original transformation matrix to simplify working with 3D renderers + * and libraries such as PeasyCam. + * + * @author Peter Lager + * + */ +public class GWindowInfo implements PConstants, GConstants, GConstantsInternal { + + public PApplet app; + public boolean app_g_3d; + public PMatrix orgMatrix; + + public LinkedList windowControls = new LinkedList(); + // These next two lists are for controls that are to be added or remove since these + // actions must be performed outside the draw cycle to avoid concurrent modification + // exceptions when changing windowControls + public LinkedList toRemove = new LinkedList(); + public LinkedList toAdd = new LinkedList(); + + // Set this to true if papplet is a GWinApplet objects i.e. part of a + // Gwindow object + boolean isGWindow; + + + /** + * Create an applet info object + * @param papplet + */ + public GWindowInfo (PApplet papplet) { + app = papplet; + // Is this applet part of a GWindow object + isGWindow = (app instanceof GWinApplet); + app_g_3d = app.g.is3D(); + if(app.g.is3D()) + orgMatrix = papplet.getMatrix((PMatrix3D)null); +// else +// orgMatrix = papplet.getMatrix((PMatrix2D)null); + + /* + * The WinInfo object is responsible for capturing events from Processing + * and passing them onto the GWindow objects and the controls. + */ + app.registerMethod("draw",this); + app.registerMethod("mouseEvent",this); + app.registerMethod("keyEvent",this); + app.registerMethod("pre",this); + app.registerMethod("post",this); + } + + /** + * The draw method registered with Processing + */ + public void draw(){ + app.pushMatrix(); + if(app_g_3d) { + app.hint(PConstants.DISABLE_DEPTH_TEST); + // Load the identity matrix. + app.resetMatrix(); + // Apply the original Processing transformation matrix. + app.applyMatrix(orgMatrix); + } + for(GAbstractControl control : windowControls){ + if( (control.registeredMethods & DRAW_METHOD) == DRAW_METHOD ) + control.draw(); + } + if(app_g_3d) + app.hint(PConstants.ENABLE_DEPTH_TEST); + app.popMatrix(); + } + + /** + * The mouse method registered with Processing + * + * If this is a secondary window then it should call the draw method for the window. + * + * Should call the user defined method for the the draw method for the actual GWindow object. + * + * @param event + */ + public void mouseEvent(MouseEvent event){ + if(isGWindow) + ((GWinApplet)app).mouseEvent(event); + for(GAbstractControl control : windowControls){ + if((control.registeredMethods & MOUSE_METHOD) == MOUSE_METHOD) + control.mouseEvent(event); + } + } + + /** + * The key method registered with Processing + */ + public void keyEvent(KeyEvent event) { + if(isGWindow) + ((GWinApplet)app).keyEvent(event); + for(GAbstractControl control : windowControls){ + if( (control.registeredMethods & KEY_METHOD) == KEY_METHOD) + control.keyEvent(event); + } + } + + /** + * The pre method registered with Processing + */ + public void pre(){ + if(GAbstractControl.controlToTakeFocus != null && GAbstractControl.controlToTakeFocus.getPApplet() == app){ + GAbstractControl.controlToTakeFocus.setFocus(true); + GAbstractControl.controlToTakeFocus = null; + } + if(isGWindow) + ((GWinApplet)app).pre(); + for(GAbstractControl control : windowControls){ + if( (control.registeredMethods & PRE_METHOD) == PRE_METHOD) + control.pre(); + } + } + + /** + * The post method registered with Processing + */ + public void post(){ + if(G4P.cursorChangeEnabled){ + if(GAbstractControl.cursorIsOver != null ) //&& GAbstractControl.cursorIsOver.getPApplet() == app) + app.cursor(GAbstractControl.cursorIsOver.cursorOver); + else + app.cursor(G4P.mouseOff); + } + if(isGWindow) + ((GWinApplet)app).post(); + for(GAbstractControl control : windowControls){ + if( (control.registeredMethods & POST_METHOD) == POST_METHOD) + control.post(); + } + // ===================================================================================================== + // ===================================================================================================== + // This is where components are removed or added to the window to avoid concurrent access violations + // ===================================================================================================== + // ===================================================================================================== + synchronized (this) { + // Dispose of any unwanted controls + if(!toRemove.isEmpty()){ + for(GAbstractControl control : toRemove){ + // If the control has focus then lose it + if(GAbstractControl.focusIsWith == control) + control.loseFocus(null); + // Clear control resources + control.buffer = null; + if(control.parent != null){ + control.parent.children.remove(control); + control.parent = null; + } + if(control.children != null) + control.children.clear(); + control.palette = null; + control.jpalette = null; + control.eventHandlerObject = null; + control.eventHandlerMethod = null; + control.winApp = null; + windowControls.remove(control); + System.gc(); + } + toRemove.clear(); + } + if(!toAdd.isEmpty()){ + for(GAbstractControl control : toAdd) + windowControls.add(control); + toAdd.clear(); + Collections.sort(windowControls, G4P.zorder); + } + } + } + + /** + * Dispose of this WIndow.
+ * First unregister for event handling then clear list of controls + * for this window. + */ + void dispose(){ + app.noLoop(); + app.unregisterMethod("draw", this); + app.unregisterMethod("pre", this); + app.unregisterMethod("post", this); + app.unregisterMethod("mouseEvent",this); + app.unregisterMethod("keyEvent",this); + windowControls.clear(); + } + + /** + * If a control is to be added to this window then add the control + * to the toAdd list. The control will actually be added during the + * post() method + * @param control + */ + synchronized void addControl(GAbstractControl control){ + // Make sure we avoid duplicates + if(!windowControls.contains(control) && !toAdd.contains(control)) + toAdd.add(control); + } + + /** + * If a control is to be removed to this window then add the control + * to the toAdd list. The control will actually be added during the + * post() method + * @param control + */ + synchronized void removeControl(GAbstractControl control){ + // Make sure we avoid duplicates + if(windowControls.contains(control) && !toRemove.contains(control)) + toRemove.add(control); + } + + void setColorScheme(int cs){ + for(GAbstractControl control : windowControls) + control.setLocalColorScheme(cs); + } + + void setAlpha(int alpha){ + for(GAbstractControl control : windowControls) + control.setAlpha(alpha); + } + + + +} \ No newline at end of file diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/HotSpot.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/HotSpot.java new file mode 100644 index 0000000..e293cff --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/HotSpot.java @@ -0,0 +1,222 @@ +package g4p_controls; + +import processing.core.PApplet; +import processing.core.PImage; + +/** + * Base class for different types of hot spot. + * + * @author Peter Lager + * + */ +abstract class HotSpot implements GConstants, Comparable { + + public final Integer id; + public float x, y; + + abstract public boolean contains(float px, float py); + + protected HotSpot(int id){ + this.id = Math.abs(id); + } + + public void adjust(Object ... arguments){} + + public int compareTo(HotSpot spoto) { + return id.compareTo(spoto.id); + } + + /** + * Hit is based on being inside a rectangle. + * + * @author Peter Lager + */ + static class HSrect extends HotSpot { + public float w, h; + + public HSrect(int id, float x, float y, float w, float h) { + super(id); + this.x = x; + this.y = y; + this.w = w; + this.h = h; + } + + @Override + public boolean contains(float px, float py) { + return (px >= x && py >= y && px <= x + w && py <= y + h); + } + } + + /** + * Hit based on an arc (pie slice) + * + * @author Peter Lager + * + */ + static class HSarc extends HotSpot { + + public float sa, ea, r, r2; + + public HSarc(int id, float x, float y, float r, float sa, float ea) { + super(id); + this.x = x; + this.y = y; + this.r = r; + this.r2 = r * r; + this.sa = sa; + this.ea = ea; + } + + + @Override + public boolean contains(float px, float py) { + if((px-x)*(px-x) + (py-y)*(py-y) > r2) + return false; + // Now check angle + float a = (float) Math.toDegrees(Math.atan2(py-y, px-x)); + if(a < 0) a += 360; + if(a < sa) a += 360; + return (a >= sa && a <= ea); + } + } + + /** + * Hit is based on being inside a rectangle. + * + * @author Peter Lager + */ + static class HScircle extends HotSpot { + public float r, r2; + + public HScircle(int id, float x, float y, float r) { + super(id); + this.x = x; + this.y = y; + this.r = r; + this.r2 = r * r; + } + + @Override + public boolean contains(float px, float py) { + return ((px-x)*(px-x) + (py-y)*(py-y) <= r2); + } + + /** + * If used the parameters must be in the order x, y then r.
+ * + */ + @SuppressWarnings(value={"fallthrough"}) + public void adjust(Object ... arguments){ + switch(arguments.length){ + case 3: + r = Float.valueOf(arguments[2].toString()); + r2 = r * r; + case 2: + y = Float.valueOf(arguments[1].toString()); + case 1: + x = Float.valueOf(arguments[0].toString()); + } + } + + + public String toString(){ + return "HS circle ["+x+", "+y+"] radius = "+r; + } + } + + /** + * Hit depends on the mask image. non-transparent areas are black and + * transparent areas are white.
+ * + * It is better this way because scaling the image can change the + * colour white to very nearly white but black is unchanged so is + * easier to test. + * + * @author Peter Lager + * + */ + static class HSmask extends HotSpot { + + private PImage mask = null; + + protected HSmask(int id, PImage mask) { + super(id); + this.mask = mask; + } + + @Override + public boolean contains(float px, float py) { + if(mask != null){ + int pixel = mask.get((int)px, (int)py); + float alpha = (pixel >> 24) & 0xff; + // A > 0 and RGB = 0 is transparent + if(alpha > 0 && (pixel & 0x00ffffff) == 0){ + return true; + } + } + return false; + } + + public String toString(){ + return "HS mask ["+x+", "+y+"]"; + } + + } + + /** + * Hit is determined by the alpha channel value. + * @author Peter + * + */ + static class HSalpha extends HotSpot { + + private PImage image = null; + + private int offX, offY; + + protected HSalpha(int id, float x, float y, PImage image, int imageMode) { + super(id); + this.image = image; + this.x = x; + this.y = y; + if(imageMode == PApplet.CENTER){ + offX = -image.width/2; + offY = -image.height/2; + } + else + offX = offY = 0; + } + + /** + * If used the parameters must be in the order x, y then image.
+ */ + public void adjust(Object ... arguments){ + switch(arguments.length){ + case 3: + image = (PImage) arguments[2]; + case 2: + y = Float.valueOf(arguments[1].toString()); + case 1: + x = Float.valueOf(arguments[0].toString()); + } + } + + @Override + public boolean contains(float px, float py) { + if(image != null){ + int imgX = Math.round(px - x) - offX; + int imgY = Math.round(py - y) - offY; + float alpha = (image.get(imgX, imgY) >> 24) & 0xff; + if(alpha > ALPHA_PICK) + return true; + } + return false; + } + + public String toString(){ + return "HS alpha ["+x+", "+y+"]"; + } + + } +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/ImageManager.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/ImageManager.java new file mode 100644 index 0000000..e07c8bd --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/ImageManager.java @@ -0,0 +1,106 @@ +package g4p_controls; + +import java.util.HashMap; + +import processing.core.PApplet; +import processing.core.PImage; + +/** + * This class is used to load bitmap files and create images.
+ * + * Although there maybe multiple requests for a particular bitmap file only + * one PImage is created for each file. + * + * @author Peter Lager + * + */ +public class ImageManager { + + private static HashMap textures = new HashMap(); + + /** + * Load a single bitmap file return a reference to the PImage created. + * + * @param app + * @param filename + * @return null if the file does not exist else the PImage object + */ + public static PImage loadImage(PApplet app, String filename){ + if(textures.containsKey(filename)){ + return textures.get(filename); + } + PImage image = app.loadImage(filename); + if(image != null){ + textures.put(filename, image); + } + else + PApplet.println("Unable to load image from file '" + filename+"'"); + return image; + } + + /** + * Load images from multiple files + * @param app + * @param filename an array of filenames + * @return an array of images + */ + public static PImage[] loadImage(PApplet app, String[] filename){ + PImage[] images = new PImage[filename.length]; + for(int i = 0; i < images.length; i++) + images[i] = loadImage(app, filename[i]); + return images; + } + + + + /** + * Make multiple images from a given image. This method creates + * a 2D array (size [nCols, nRows] ) of PImage objects. + * + * @param app + * @param img the tiled image + * @param nCols number of tiles across + * @param nRows number of tiles down + * @return a 2D array of images (tiles) + */ + public static PImage[][] makeTiles2D(PApplet app, PImage img, int nCols, int nRows){ + PImage[][] imageTiles = new PImage[nCols][nRows]; + int tileW = img.width / nCols; + int tileH = img.height / nRows; + for(int y = 0; y < nRows; y++){ + for(int x = 0; x < nCols; x++){ + imageTiles[x][y] = app.createImage(tileW, tileH, PApplet.ARGB); + imageTiles[x][y].copy(img, x * tileW, y * tileH, tileW, tileH, 0, 0, tileW, tileH); + } + } + return imageTiles; + } + + /** + * Make multiple images from a given image. This method creates + * a 1D array of PImage objects. The order is left-right and top-down. + * + * @param app + * @param img the tiled image + * @param nCols number of tiles across + * @param nRows number of tiles down + * @return a 1D array of images (tiles) + */ + public static PImage[] makeTiles1D(PApplet app, PImage img, int nCols, int nRows){ + PImage[] imageTiles = new PImage[nCols * nRows]; + int tileW = img.width / nCols; + int tileH = img.height / nRows; + int tileNo = 0; + for(int y = 0; y < nRows; y++){ + for(int x = 0; x < nCols; x++){ + imageTiles[tileNo] = app.createImage(tileW, tileH, PApplet.ARGB); + imageTiles[tileNo].copy(img, x * tileW, y * tileH, tileW, tileH, 0, 0, tileW, tileH); + tileNo++; + } + } + return imageTiles; + } + + + +} diff --git a/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/StyledString.java b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/StyledString.java new file mode 100644 index 0000000..db851de --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/G4P/src/g4p_controls/StyledString.java @@ -0,0 +1,1094 @@ +/* + Part of the GUI for Processing library + http://www.lagers.org.uk/g4p/index.html + http://gui4processing.googlecode.com/svn/trunk/ + + Copyright (c) 2008-13 Peter Lager + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package g4p_controls; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.font.FontRenderContext; +import java.awt.font.GraphicAttribute; +import java.awt.font.ImageGraphicAttribute; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextHitInfo; +import java.awt.font.TextLayout; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.text.AttributedCharacterIterator; +import java.text.AttributedCharacterIterator.Attribute; +import java.text.AttributedString; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.ListIterator; + +import processing.core.PApplet; + +/** + * This class is used to represent text with attributes.
+ * It means that you don't have to have the same style of font + * or even the same font face over the whole length of the text.
+ * + * Most font features can be modified all except the text background + * which is transparent. There is a feature to highlight part of the string + * by having a different background colour but this is used for highlighting + * selected text in GTextField and GTextArea components.
+ * + * It is also used for all controls that use text. + * @author Peter Lager + * + */ +final public class StyledString implements GConstantsInternal, Serializable { + + private static final long serialVersionUID = 3577177485527475867L; + + transient private AttributedString styledText = null; + transient private ImageGraphicAttribute spacer = null; + transient private LineBreakMeasurer lineMeasurer = null; + transient private LinkedList linesInfo = new LinkedList(); + transient private Font font = null; + + // The plain text to be styled + private String plainText = ""; + // List of attribute runs to match font + private LinkedList baseStyle = new LinkedList(); + // List of attribute runs to be applied over the base style + private LinkedList atrun = new LinkedList(); + + // The width to break a line + private int wrapWidth = Integer.MAX_VALUE; + // Flag to determine whether the text layouts need recalculating + private boolean invalidLayout = true; + // Flag to determine whether the actual character string have changed + private boolean invalidText = true; + + // Base justification + private boolean justify = false; + private float justifyRatio = 0.7f; + + // Stats + private float textHeight = 0; + private float maxLineLength = 0; + private float maxLineHeight = 0; + private int nbrLines; + + // These are only used by GTextField and GTextArea to store the start and end positions + // for selected text when the string is to be saved. + int startIdx = -1; + int endIdx = -1; + + /** + * This is assumed to be a single line of text (i.e. no wrap). + * EOL characters will be stripped from the text before use. + * + * @param startText + */ + public StyledString(String startText){ + plainText = removeSingleSpacingFromPlainText(startText); + spacer = getParagraghSpacer(1); // safety + // Get rid of any EOLs + styledText = new AttributedString(plainText); + applyAttributes(); + invalidText = true; + invalidLayout = true; + } + + /** + * Supports multiple lines of text wrapped on word boundaries.
+ * + * @param startText + * @param wrapWidth + */ + public StyledString(String startText, int wrapWidth){ + if(wrapWidth > 0 && wrapWidth < Integer.MAX_VALUE) + this.wrapWidth = wrapWidth; + plainText = (wrapWidth == Integer.MAX_VALUE) ? removeSingleSpacingFromPlainText(startText) : removeDoubleSpacingFromPlainText(startText); + spacer = getParagraghSpacer(this.wrapWidth); + styledText = new AttributedString(plainText); + styledText = insertParagraphMarkers(plainText, styledText); + applyAttributes(); + invalidText = true; + invalidLayout = true; + } + + /** + * Converts this StyledString from multi-line to single-line by replacing all EOL + * characters with the space character + * for paragraphs + * @param ptext + * @param as + * @return the converted string + */ + StyledString convertToSingleLineText(){ + // Make sure we have something to work with. + if(styledText == null || plainText == null){ + plainText = ""; + styledText = new AttributedString(plainText); + } + else { + // Scan through plain text and for each EOL replace the paragraph spacer from + // the attributed string (styledText). + int fromIndex = plainText.indexOf('\n', 0); + if(fromIndex >= 0){ + while(fromIndex >= 0){ + try { // if text == "\n" then an exception is thrown + styledText.addAttribute(TextAttribute.CHAR_REPLACEMENT, ' ', fromIndex, fromIndex + 1); + fromIndex = plainText.indexOf('\n', fromIndex + 1); + } + catch(Exception excp){ + break; + } + } + // Finally replace all EOL in the plainText + plainText = plainText.replace('\n', ' '); + } + } + wrapWidth = Integer.MAX_VALUE; + return this; + } + + /** + * Get the plain text as a String. Any line breaks will kept and will + * be represented by the character 'backslash n'
+ * @return the associated plain text + */ + public String getPlainText(){ + return plainText; + } + + /** + * Get the number of characters in this styled string + */ + public int length(){ + return plainText.length(); + } + + /* + * Set the font face to be used. This will be system specific + * but all Java implementations support the following logical + * fonts.
+ * Dialog
+ * DialogInput
+ * Monospaced
+ * Serif
+ * SansSerif
+ * and the physical font faces for
+ * Lucinda
+ * Requesting a font family that does not existing on the system + * will be ignored.
+ * Note the class attribute fontFamilies is an array of all + * @param family + * @return true if the font was found and different from the current font family + */ + + /** + * Text can be either left or fully justified. + * @param justify true for full justification + */ + public void setJustify(boolean justify){ + if(this.justify != justify){ + this.justify = justify; + invalidLayout = true; + } + } + + /** + * Justify only if the line has sufficient text to do so. + * + * @param jRatio ratio of text length to visibleWidth + */ + public void setJustifyRatio(float jRatio){ + if(justifyRatio != jRatio){ + justifyRatio = jRatio; + if(justify) + invalidLayout = true; + } + } + + /** + * This class uses transparent images to simulate end/starting positions + * for paragraphs + * @param ptext + * @param as + * @return the styled string with paragraph marker images embedded + */ + private AttributedString insertParagraphMarkers(String ptext, AttributedString as ){ + if(ptext != null && ptext.length() > 0) + plainText = ptext; + int fromIndex = ptext.indexOf('\n', 0); + while(fromIndex >= 0){ + try { // if text == "\n" then an exception is thrown + as.addAttribute(TextAttribute.CHAR_REPLACEMENT, spacer, fromIndex, fromIndex + 1); + fromIndex = ptext.indexOf('\n', fromIndex + 1); + } + catch(Exception excp){ + break; + } + } + return as; + } + + /** + * Add an attribute that affects the whole length of the string. + * @param type attribute type + * @param value attribute value + */ + public void addAttribute(Attribute type, Object value){ + addAttribute(type, value, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + /** + * Set charStart and charEnd to <0 if full length. + * + * @param type + * @param value + * @param charStart + * @param charEnd + */ + public void addAttribute(Attribute type, Object value, int charStart, int charEnd){ + AttributeRun ar = new AttributeRun(type, value, charStart, charEnd); + // If we already have attributes try and rationalise the number by merging + // runs if possible and removing runs that no longer have a visible effect. + if(atrun.size() > 0){ + ListIterator iter = atrun.listIterator(atrun.size()); + while(iter.hasPrevious()){ + AttributeRun a = iter.previous(); + int action = ar.intersectionWith(a); + int intersect = action & I_MODES; + int combiMode = action & COMBI_MODES; + if(combiMode == MERGE_RUNS){ + switch(intersect){ + case I_TL: + case I_CL: + ar.start = a.start; + iter.remove(); + break; + case I_TR: + case I_CR: + ar.end = a.end; + iter.remove(); + break; + } + } + else if(combiMode == CLIP_RUN){ + switch(intersect){ + case I_CL: + a.end = ar.start; + break; + case I_CR: + a.start = ar.end; + break; + } + } + switch(intersect){ + case I_INSIDE: + iter.remove(); + break; + case I_COVERED: + ar = null; + break; + } + } + } + // If the run is still effective then add it + if(ar != null) + atrun.addLast(ar); + applyAttributes(); + invalidLayout = true; + } + + + /** + * Must call this method to apply + */ + private void applyAttributes(){ + if(plainText.length() > 0){ + for(AttributeRun bsar : baseStyle) + styledText.addAttribute(bsar.atype, bsar.value); + Iterator iter = atrun.iterator(); + AttributeRun ar; + while(iter.hasNext()){ + ar = iter.next(); + if(ar.end == Integer.MAX_VALUE) + styledText.addAttribute(ar.atype, ar.value); + else { + // If an attribute run fails do not try and fix it - dump it + try { + styledText.addAttribute(ar.atype, ar.value, ar.start, ar.end); + } + catch(Exception excp){ + System.out.println("Dumping " + ar); + iter.remove(); + } + } + } + } + invalidLayout = true; + } + + /** + * Clears all attributes from start to end-1 + * @param start + * @param end + */ + public void clearAttributes(int start, int end){ + ListIterator iter = atrun.listIterator(); + AttributeRun ar; + while(iter.hasNext()){ + ar = iter.next(); + // Make sure we have intersection + if( !(start >= ar.end && end >= ar.start )){ + // Find the limits to clear + int s = Math.max(start, ar.start); + int e = Math.min(end, ar.end); + if(ar.start == s && ar.end == e) + iter.remove(); + else if(ar.start == s) // clear style from beginning + ar.start = e; + else if(ar.end == e) // clear style from end + ar.end = s; + else { // Split attribute run + AttributeRun ar2 = new AttributeRun(ar.atype, ar.value, e, ar.end); + iter.add(ar2); + ar.end = s; + } + } + } + invalidText = true; + } + + /** + * Removes all styling from the string. + * + */ + public void clearAllAttributes(){ + atrun.clear(); + invalidText = true; + } + + /** + * Insert 1 or more characters into the string. The inserted text will first be made + * safe by removing any inappropriate EOL characters.
+ * Do not use this method to insert EOL characters, use the
insertEOL(int)
+ * method instead. + * + * @param chars + * @param insertPos the position in the text + */ + public int insertCharacters(int insertPos, String chars){ + if(chars.length() > 0) + chars = makeStringSafeForInsert(chars); + return insertCharactersImpl(insertPos, chars, false); +// if(chars.length() > 0){ +// int nbrChars = chars.length(); +// if(plainText.equals(" ")) +// plainText = chars; +// else +// plainText = plainText.substring(0, insertPos) + chars + plainText.substring(insertPos); +// insertParagraphMarkers(plainText, styledText); +// for(AttributeRun ar : atrun){ +// if(ar.end < Integer.MAX_VALUE){ +// if(ar.end >= insertPos){ +// ar.end += nbrChars; +// if(ar.start >= insertPos) +// ar.start += nbrChars; +// } +// } +// } +// invalidText = true; +// } +// return chars.length(); + } + + public int insertCharacters(int insertPos, String chars, boolean startNewLine){ + // Ray: modify the code to not call makeStringSafeForInsert as it will + // remove EOL characters. + //if(chars.length() > 0) + // chars = makeStringSafeForInsert(chars); + return insertCharactersImpl(insertPos, chars, startNewLine); + } + + private int insertCharactersImpl(int insertPos, String chars, boolean startNewLine){ + if(chars.length() > 0){ + if(startNewLine) + chars = "\n" + chars; + int nbrChars = chars.length(); + if(plainText.equals(" ")) + plainText = chars; + else + plainText = plainText.substring(0, insertPos) + chars + plainText.substring(insertPos); + insertParagraphMarkers(plainText, styledText); + for(AttributeRun ar : atrun){ + if(ar.end < Integer.MAX_VALUE){ + if(ar.end >= insertPos){ + ar.end += nbrChars; + if(ar.start >= insertPos) + ar.start += nbrChars; + } + } + } + invalidText = true; + } + return chars.length(); + } + + /** + * This is ONLY used when multiple characters are to be inserted.
+ * If it is single line text i.e. no wrapping then it removes all EOLs + * If it is multiple line spacing it will reduce all double EOLs to single + * EOLs and remove any EOLs at the start or end of the string. + * + * @param chars + * @return a string that is safe for inserting + */ + private String makeStringSafeForInsert(String chars){ + // Get rid of single / double line spacing + if(chars.length() > 0){ + if(wrapWidth == Integer.MAX_VALUE) // no wrapping remove all + chars = removeSingleSpacingFromPlainText(chars); + else { + chars = removeDoubleSpacingFromPlainText(chars); // wrapping remove double spacing + // no remove EOL at ends of string + while(chars.length() > 0 && chars.charAt(0) == '\n') + chars = chars.substring(1); + while(chars.length() > 0 && chars.charAt(chars.length() - 1) == '\n') + chars = chars.substring(0, chars.length() - 1); + } + } + return chars; + } + + /** + * Use this method to insert an EOL character. + * @param insertPos index position to insert EOL + * @return true if an EOL was inserted into the string + */ + public boolean insertEOL(int insertPos){ + if(wrapWidth != Integer.MAX_VALUE){ + if(insertPos > 0 && plainText.charAt(insertPos-1) == '\n') + return false; + if(insertPos < plainText.length()-1 && plainText.charAt(insertPos+1) == '\n'){ + return false; + } + plainText = plainText.substring(0, insertPos) + "\n" + plainText.substring(insertPos); + insertParagraphMarkers(plainText, styledText); + for(AttributeRun ar : atrun){ + if(ar.end < Integer.MAX_VALUE){ + if(ar.end >= insertPos){ + ar.end += 1; + if(ar.start >= insertPos) + ar.start += 1; + } + } + } + invalidText = true; + return true; + } + return false; + } + + + /** + * Remove a number of characters from the string + * + * @param nbrToRemove number of characters to remove + * @param fromPos start location for removal + * @return true if the deletion was successful else false + */ + public boolean deleteCharacters(int fromPos, int nbrToRemove){ + if(fromPos < 0 || fromPos + nbrToRemove > plainText.length()) + return false; + /* + * If the character preceding the selection and the character immediately after the selection + * are both EOLs then increment the number of characters to be deleted + */ + if(wrapWidth != Integer.MAX_VALUE){ + if(fromPos > 0 && fromPos + nbrToRemove < plainText.length() - 1){ + if(plainText.charAt(fromPos) == '\n' && plainText.charAt(fromPos + nbrToRemove) == '\n'){ + nbrToRemove++; + } + } + } + if(fromPos != 0) + plainText = plainText.substring(0, fromPos) + plainText.substring(fromPos + nbrToRemove); + else + plainText = plainText.substring(fromPos + nbrToRemove); + // For wrappable text make sure we have not created + if(plainText.length() == 0){ + atrun.clear(); + styledText = null; + } + else { + ListIterator iter = atrun.listIterator(atrun.size()); + AttributeRun ar; + while(iter.hasPrevious()){ + ar = iter.previous(); + if(ar.end < Integer.MAX_VALUE){ + // Only need to worry about this if the run ends after the deletion point + if(ar.end >= fromPos){ + int lastPos = fromPos + nbrToRemove; + // Deletion removes entire run + if(fromPos <= ar.start && lastPos >= ar.end){ + iter.remove(); + continue; + } + // Deletion fits entirely within the run + if(fromPos > ar.start && lastPos < ar.end){ + ar.end -= nbrToRemove; + continue; + } + // Now we have overlap either at one end of the run + // Overlap at start of run? + if(fromPos <= ar.start){ + ar.start = fromPos; + ar.end -= nbrToRemove; + continue; + } + // Overlap at end of run? + if(lastPos >= ar.end){ + ar.end = fromPos; + continue; + } + System.out.println("This run was not modified"); + System.out.println("Run from " + ar.start + " to " + ar.end); + System.out.println("Delete from " + fromPos + " To " + lastPos + " (" + nbrToRemove + " to remove)"); + } + } + } + } + invalidText = true; + return true; + } + + public void setFont(Font a_font){ + if(a_font != null){ + font = a_font; + baseStyle.clear(); + baseStyle.add(new AttributeRun(TextAttribute.FAMILY, font.getFamily())); + baseStyle.add(new AttributeRun(TextAttribute.SIZE, font.getSize())); + if(font.isBold()) + baseStyle.add(new AttributeRun(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD)); + if(font.isItalic()) + baseStyle.add(new AttributeRun(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE)); + invalidText = true; + } + } + + /** + * Get the text layouts for display if the string has changed since last call + * to this method regenerate them. + * + * @param g2d Graphics2D display context + * @return a list of text layouts for rendering + */ + public LinkedList getLines(Graphics2D g2d){ + if(font != g2d.getFont()){ + setFont(g2d.getFont()); + invalidText = true; + } + if(invalidText){ + styledText = new AttributedString(plainText); + styledText = insertParagraphMarkers(plainText, styledText); + applyAttributes(); + invalidText = false; + invalidLayout = true; + } + if(invalidLayout){ + linesInfo.clear(); + if(plainText.length() > 0){ + textHeight = 0; + maxLineLength = 0; + maxLineHeight = 0; + nbrLines = 0; + AttributedCharacterIterator paragraph = styledText.getIterator(null, 0, plainText.length()); + FontRenderContext frc = g2d.getFontRenderContext(); + lineMeasurer = new LineBreakMeasurer(paragraph, frc); + float yposinpara = 0; + int charssofar = 0; + while (lineMeasurer.getPosition() < plainText.length()) { + TextLayout layout = lineMeasurer.nextLayout(wrapWidth); + float advance = layout.getVisibleAdvance(); + if(justify){ + if(justify && advance > justifyRatio * wrapWidth){ + //System.out.println(layout.getVisibleAdvance() + " " + breakWidth + " "+ layout.get); + // If advance > breakWidth then we have a line break + float jw = (advance > wrapWidth) ? advance - wrapWidth : wrapWidth; + layout = layout.getJustifiedLayout(jw); + } + } + // Remember the longest and tallest value for a layout so far. + float lh = getHeight(layout); + if(lh > maxLineHeight) + maxLineHeight = lh; + textHeight += lh; + if(advance <= wrapWidth && advance > maxLineLength) + maxLineLength = advance; + + // Store layout and line info + linesInfo.add(new TextLayoutInfo(nbrLines, layout, charssofar, layout.getCharacterCount(), yposinpara)); + charssofar += layout.getCharacterCount(); + yposinpara += lh; + nbrLines++; + } + } + invalidLayout = false; + } + return linesInfo; + } + + /** + * Get the number of lines in the layout + */ + public int getNbrLines(){ + return nbrLines; + } + + /** + * Return the height of the text line(s) + */ + public float getTextAreaHeight(){ + return textHeight; + } + + /** + * Return the length of the longest line. + */ + public float getMaxLineLength(){ + return maxLineLength; + } + + /** + * Get the height of the tallest line + */ + public float getMaxLineHeight(){ + return maxLineHeight; + } + + /** + * Get the height of the given TextLayout + * @param layout + * @return the height of a given text layout + */ + private float getHeight(TextLayout layout){ + return layout.getAscent() +layout.getDescent() + layout.getLeading(); + } + + /** + * Get the break width used to create the lines. + */ + public int getWrapWidth(){ + return wrapWidth; + } + + /** + * Set the maximum width of a line. + * @param wrapWidth + */ + public void setWrapWidth(int wrapWidth){ + if(this.wrapWidth != wrapWidth){ + this.wrapWidth = wrapWidth; + invalidLayout = true; + } + } + + TextLayoutHitInfo calculateFromXY(Graphics2D g2d, float px, float py){ + TextHitInfo thi = null; + TextLayoutInfo tli = null; + TextLayoutHitInfo tlhi = null; + if(invalidLayout) + getLines(g2d); + if(px < 0) px = 0; + if(py < 0) py = 0; + tli = getTLIforYpos(py); + // Correct py to match layout's upper-left bounds + py -= tli.yPosInPara; + // get hit + thi = tli.layout.hitTestChar(px,py); + tlhi = new TextLayoutHitInfo(tli, thi); + return tlhi; + } + + /** + * Get a layout based on line number + * @param ln line number + * @return text layout info for the line ln + */ + TextLayoutInfo getTLIforLineNo(int ln){ + return linesInfo.get(ln); + } + + /** + * This will always return a layout provide there is some text. + * @param y Must be >= 0 + * @return the first layout where y is above the upper layout bounds + */ + TextLayoutInfo getTLIforYpos(float y){ + TextLayoutInfo tli = null; + if(!linesInfo.isEmpty()){ + for(int i = linesInfo.size()-1; i >= 0; i--){ + tli = linesInfo.get(i); + if(tli.yPosInPara <= y) + break; + } + } + return tli; + } + + /** + * This will always return a layout provided charNo >= 0.
+ * + * If charNo > than the index of the last character in the plain text then this + * should be corrected to the last character in the layout by the caller. + * + * @param charNo the character position in text (must be >= 0) + * @return the first layout where c is greater that the layout's start char index. + */ + TextLayoutInfo getTLIforCharNo(int charNo){ + TextLayoutInfo tli = null; + if(!linesInfo.isEmpty()){ + for(int i = linesInfo.size()-1; i >= 0; i--){ + tli = linesInfo.get(i); + if(tli.startCharIndex < charNo) + break; + } + } + return tli; + } + + /** + * Ensure we do not have blank lines by replacing double EOL characters by + * single EOL until there are only single EOLs.
+ * Using replaceAll on its own will not work because EOL/EOL/EOL would + * become EOL/EOL not the single EOL required. + * + */ + private String removeDoubleSpacingFromPlainText(String chars){ + while(chars.indexOf("\n\n") >= 0){ + invalidText = true; + chars = chars.replaceAll("\n\n", "\n"); + } + return chars; + } + + /** + * Remove all EOL characters from the string. This is necessary if the string + * is for a single line component. + * @param chars the string to use + * @return the string with all EOLs removed + */ + private String removeSingleSpacingFromPlainText(String chars){ + while(chars.indexOf("\n") >= 0){ + invalidText = true; + chars = chars.replaceAll("\n", ""); + } + return chars; + } + + /** + * Create a graphic image character to simulate paragraph breaks + * + * @param ww + * @return a blank image to manage paragraph ends. + */ + private ImageGraphicAttribute getParagraghSpacer(int ww){ + if(ww == Integer.MAX_VALUE) + ww = 1; + BufferedImage img = new BufferedImage(ww, 10, BufferedImage.TYPE_INT_ARGB); + Graphics g = img.getGraphics(); + g.setColor(new Color(255, 255, 255, 0)); + g.fillRect(0,0,img.getWidth(), img.getHeight()); + return new ImageGraphicAttribute(img, GraphicAttribute.TOP_ALIGNMENT); + } + + + /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + * Serialisation routines to save/restore the StyledString to disc. + */ + + /** + * Save the named StyleString in the named file. + * + * @param papp + * @param ss the styled string + * @param fname + */ + public static void save(PApplet papp, StyledString ss, String fname){ + OutputStream os; + ObjectOutputStream oos; + try { + os = papp.createOutput(fname); + oos = new ObjectOutputStream(os); + oos.writeObject(ss); + os.close(); + oos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Load and return a StyledString object from the given file. + * + * @param papp + * @param fname the filename of the StyledString + */ + public static StyledString load(PApplet papp, String fname){ + StyledString ss = null; + InputStream is; + ObjectInputStream ios; + try { + is = papp.createInput(fname); + ios = new ObjectInputStream(is); + ss = (StyledString) ios.readObject(); + is.close(); + ios.close(); + } catch (IOException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return ss; + } + + + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException { + ois.defaultReadObject(); + // Recreate transient elements + spacer = getParagraghSpacer(wrapWidth); + styledText = new AttributedString(plainText); + styledText = insertParagraphMarkers(plainText, styledText); + linesInfo = new LinkedList(); + applyAttributes(); + } + + /** + * For multi-line text, the TextHitInfo class is not enough. We also need + * information about the layout so that the caret(s) can be drawn. + * + * @author Peter Lager + * + */ + static public class TextLayoutHitInfo implements Comparable{ + public TextLayoutInfo tli; + public TextHitInfo thi; + + + public TextLayoutHitInfo() { + this.tli = null; + this.thi = null; + } + + /** + * @param tli + * @param thi + */ + public TextLayoutHitInfo(TextLayoutInfo tli, TextHitInfo thi) { + this.tli = tli; + this.thi = thi; + } + + /** + * Copy constructor + * @param tlhi + */ + public TextLayoutHitInfo(TextLayoutHitInfo tlhi){ + tli = tlhi.tli; + thi = tlhi.thi; + } + + public void copyFrom(TextLayoutHitInfo other){ + this.tli = other.tli; + this.thi = other.thi; + } + + public void setInfo(TextLayoutInfo tli, TextHitInfo thi) { + this.tli = tli; + this.thi = thi; + } + + public int compareTo(TextLayoutHitInfo other) { + int layoutComparison = tli.compareTo(other.tli); + if(layoutComparison != 0) + return layoutComparison; // Different layouts so return comparison + // Same layout SO test hit info + if(thi.equals(other.thi)) + return 0; + // Same layout different hit info SO test char index + if(thi.getCharIndex() != other.thi.getCharIndex()){ + // Different current chars so order on position + return (thi.getCharIndex() < other.thi.getCharIndex() ? -1 : 1); + } + // Same layout same char different edge hit SO test on edge hit + return (thi.isLeadingEdge() ? -1 : 1); + } + + public String toString(){ + StringBuilder s = new StringBuilder(tli.toString()); + s.append(" Hit char = " + thi.getCharIndex()); + return new String(s); + } + } + + /** + * Class to hold information about a text layout. This class helps simplify the + * algorithms needed for multi-line text. + * + * @author Peter Lager + * + */ + static public class TextLayoutInfo implements Comparable { + public TextLayout layout; // The matching layout + public int lineNo; // The line number + public int startCharIndex; // Position of the first char in text + public int nbrChars; // Number of chars in this layout + public float yPosInPara; // Top-left corner of bounds + + /** + * @param startCharIndex + * @param nbrChars + * @param yPosInPara + */ + public TextLayoutInfo(int lineNo, TextLayout layout, int startCharIndex, int nbrChars, float yPosInPara) { + this.lineNo = lineNo; + this.layout = layout; + this.startCharIndex = startCharIndex; + this.nbrChars = nbrChars; + this.yPosInPara = yPosInPara; + } + + public int compareTo(TextLayoutInfo other) { + if(lineNo == other.lineNo) + return 0; + return (startCharIndex < other.startCharIndex) ? -1 : 1; + } + + public String toString(){ + StringBuilder s = new StringBuilder("{ Line no = " + lineNo + " starts @ char pos " + startCharIndex); + s.append(" last index " + (startCharIndex+nbrChars+1)); + s.append(" (" + nbrChars +") "); + return new String(s); + } + } + + /** + * Since most of the Java classes associated with AttributedString + * are immutable with virtually no public methods this class represents + * an attribute to be applied.
+ * + * This class is only used from within StyledString. + * + * @author Peter Lager + * + */ + private class AttributeRun implements Serializable { + + private static final long serialVersionUID = -8401062069478890163L; + + public Attribute atype; + public Object value; + public Integer start; + public Integer end; + + + /** + * The attribute and value to be applied over the whole string + * @param atype + * @param value + */ + public AttributeRun(Attribute atype, Object value) { + this.atype = atype; + this.value = value; + this.start = Integer.MIN_VALUE; + this.end = Integer.MAX_VALUE; + } + + /** + * The attribute and value to be applied over the given range + * @param atype + * @param value + * @param start + * @param end + */ + public AttributeRun(Attribute atype, Object value, int start, int end) { + this.atype = atype; + this.value = value; + this.start = start; + this.end = end; + } + + /** + * If possible merge the two runs or crop the prevRun run. + * + * If both runs have the same attribute type and the represent + * the same location and size in the text then the intersection + * mode will be MM_SURROUNDS rather than MM_SURROUNDED because + * 'this' is the attribute being added. + * @param m + * @param s + * @return + */ + private int intersectionWith(AttributeRun ar){ + // Different attribute types? + if(atype != ar.atype) + return I_NONE; + // Check for combination mode + int combi_mode = (value.equals(ar.value)) ? MERGE_RUNS : CLIP_RUN; + int sdx = 4, edx = 0; + // Start index + if(ar.start < start) + sdx = 0; + else if(ar.start == start) + sdx = 1; + else if (ar.start < end) + sdx = 2; + else if(ar.start == end) + sdx = 3; + if(sdx < 4){ + if(ar.end > end) + edx = 4; + else if(ar.end == end) + edx = 3; + else if(ar.end > start) + edx = 2; + else if(ar.end == start) + edx = 1; + } + combi_mode |= grid[sdx][edx]; + return combi_mode; + } + + public String toString(){ + String s = atype.toString() + " value = " + value.toString() + " from " + start + " to " + end; + return s; + } + + } // End of AttributeRun class + +} diff --git a/host software/processing libraries/sketchbook/libraries/hidapi/library.properties b/host software/processing libraries/sketchbook/libraries/hidapi/library.properties new file mode 100644 index 0000000..c23c640 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/hidapi/library.properties @@ -0,0 +1,6 @@ +name=SquareWear +category=Hardware +authorList=[Cheni Chadowitz] +sentence=Interface SquareWear (rayshobby.net) +version=1 +prettyVersion=0.1.0 diff --git a/host software/processing libraries/sketchbook/libraries/hidapi/library/hidapi.jar b/host software/processing libraries/sketchbook/libraries/hidapi/library/hidapi.jar new file mode 100644 index 0000000..55f332e Binary files /dev/null and b/host software/processing libraries/sketchbook/libraries/hidapi/library/hidapi.jar differ diff --git a/host software/processing libraries/sketchbook/libraries/hidapi/src/hidapi/hidapi.java b/host software/processing libraries/sketchbook/libraries/hidapi/src/hidapi/hidapi.java new file mode 100644 index 0000000..7000d42 --- /dev/null +++ b/host software/processing libraries/sketchbook/libraries/hidapi/src/hidapi/hidapi.java @@ -0,0 +1,332 @@ +/** + * SquareWear + * + * @author Cheni Chadowitz + * @modified 08/19/2013 + * @version 0.1.0 (1) + */ + +package SquareWear; + +import com.codeminders.hidapi.HIDDeviceInfo; +import com.codeminders.hidapi.HIDManager; +import com.codeminders.hidapi.HIDDevice; + +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; + +/** + * This is a template class and can be used to start a new processing library or + * tool. Make sure you rename this class as well as the name of the example + * package 'template' to your own library or tool naming convention. + * + * @example Hello + * + * (the tag @example followed by the name of an example included in + * folder 'examples' will automatically include the example in the + * javadoc.) + * + */ + +public class SquareWear { + + public final static String VERSION = "0.1.0"; + + private static Boolean Initialized = false; + + /** + * SquareWear vendor ID + */ + public final static int VENDOR_ID = 0x16c0; + + /** + * SquareWear product ID + */ + public final static int PRODUCT_ID = 0x05df; + + /** + * HID device object to communicate directly with SquareWear + */ + private HIDDevice device = null; + + /** + * return the version of the library. + * + * @return String + */ + public static String version() { + return VERSION; + } + + /** + * Assign HIDDevice + * + * @param device HID device object to communicate directly with SquareWear + */ + private void setDevice(HIDDevice device) { + this.device = device; + } + + /** + * Load hidapi library based on the OS. This function is automatically called whenever + * a search for SquareWear is performed for the first time. + */ + public static void Initialize() { + if (!Initialized) { + Initialized = true; + + com.codeminders.hidapi.ClassPathLibraryLoader + .loadNativeHIDLibrary(); + } + } + + /** + * Find first SquareWear connected to the computer + * + * @return SquareWear object or null if no SquareWears are connected + */ + public static SquareWear findFirst() { + Initialize(); + + HIDDeviceInfo[] infos = findAllDescriptors(); + + if (infos.length > 0) { + SquareWear result = new SquareWear(); + try { + result.setDevice(infos[0].open()); + return result; + } catch (Exception e) { + } + } + return null; + } + + /** Find SquareWear by serial number + * + * @param serial The serial number to search + * + * @return SquareWear object or null if device was not found + */ + public static SquareWear findBySerial(String serial) { + Initialize(); + + HIDDeviceInfo[] infos = findAllDescriptors(); + + for (HIDDeviceInfo info : infos) { + if (info.getSerial_number().equals(serial)) { + SquareWear result = new SquareWear(); + try { + result.setDevice(infos[0].open()); + return result; + } catch (Exception e) { + } + } + } + + return null; + } + + /** + * Find all SquareWear HIDDeviceInfo descriptions connected to the computer + * + * @return an array of HIDDeviceInfo objects with VID and PID matching SquareWear + */ + private static HIDDeviceInfo[] findAllDescriptors() { + Initialize(); + + List SquareWearList = new ArrayList(); + + try { + HIDManager hidManager = HIDManager.getInstance(); + + HIDDeviceInfo[] infos = hidManager.listDevices(); + + for (HIDDeviceInfo info : infos) { + if (info.getVendor_id() == VENDOR_ID + && info.getProduct_id() == PRODUCT_ID) { + SquareWearList.add(info); + } + } + } catch (Exception e) { + } + + return SquareWearList.toArray(new HIDDeviceInfo[SquareWearList.size()]); + } + + /** + * Find all SquareWears connected to the computer + * + * @return an array of SquareWear objects + */ + public static SquareWear[] findAll() { + List SquareWearList = new ArrayList(); + + HIDDeviceInfo[] infos = findAllDescriptors(); + + for (HIDDeviceInfo info : infos) { + SquareWear SquareWear = new SquareWear(); + try { + SquareWear.setDevice(info.open()); + SquareWearList.add(SquareWear); + } catch (Exception e) { + } + } + + return SquareWearList.toArray(new SquareWear[SquareWearList.size()]); + } + + public static void main(String[] args) { + SquareWear s = SquareWear.findFirst(); + s.write("abcdefgh"); + while(true) { + String res = s.read(); + if(res != null) System.out.print(res); + } + } + /** + * Get value of InfoBlocks + * + * @param id InfoBlock id, should be 1 or 2 as only supported info blocks + */ + public String getInfoBlock(int id) { + byte[] data = new byte[33]; + data[0] = (byte) (id + 1); + + String result = ""; + try { + int read = device.getFeatureReport(data); + System.out.println("Read: "+read); + System.out.println("0: "+data[0]); + if (read > 0) { + for (int i = 1; i < data.length; i++) { + //System.out.println(i+": "+(char)data[i]); + if (i == 0) { + break; + } + + result += (char) data[i]; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return result; + } + + /** + * Read 8 bytes of data from the device + * + * @return String of up to 8 chars, or null + */ + public String read() { + try { + device.disableBlocking(); + byte[] data = new byte[8]; + int read = device.read(data); + if(read > 0) { + String str = new String(); + for(int i=0; i= 32) { // cannot send more than 32 bytes as of now + break; + } + data[i] = (byte) charArray[i-1]; + } + try { + System.out.println("SENT: "+device.sendFeatureReport(data)); + } catch (Exception e) { + e.printStackTrace(); + + } + } + /** + * Set value for InfoBlocks + * + * @param id InfoBlock id, should be 1 or 2 as only supported info blocks + * @param value The value to be written to the info block + */ + public void setInfoBlock(int id, String value) { + char[] charArray = value.toCharArray(); + byte[] data = new byte[33]; + data[0] = (byte) (id + 1); + + for (int i = 0; i < charArray.length; i++) { + if (i > 32) { + break; + } + + data[i + 1] = (byte) charArray[i]; + } + + try { + device.sendFeatureReport(data); + } catch (Exception e) { + e.printStackTrace(); + + } + } + + /** + * Get the manufacturer of the device + * + * @return Returns the manufacturer name of the device + */ + public String getManufacturer() { + try { + return device.getManufacturerString(); + } catch (Exception e) { + return ""; + } + } + + /** + * Get the product description of the device + * + * @return Returns the product name of the device. + */ + public String getProduct() { + try { + return device.getProductString(); + } catch (Exception e) { + return ""; + } + } + + + /** + * Get the serial number of the device + * + * @return Returns the serial number of device. + */ + public String getSerial() { + try { + return device.getSerialNumberString(); + } catch (Exception e) { + return ""; + } + } + +} diff --git a/schematic/328vusb.png b/schematic/328vusb.png new file mode 100644 index 0000000..789c117 Binary files /dev/null and b/schematic/328vusb.png differ diff --git a/schematic/328vusb.sch b/schematic/328vusb.sch new file mode 100644 index 0000000..cbeb324 --- /dev/null +++ b/schematic/328vusb.sch @@ -0,0 +1,8825 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>USB Series Mini-B Hole Mounted</b> + + + + + + + + + + + + + + + + + + + + +>NAME + + + + + + +USB Series B Surface Mounted + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME + + + + +<b>USB Series Mini-B Surface Mounted</b> + + + + + + + + + + + + + + + + +>VALUE +>NAME + + + + + + + + + + + + + + + + + + + + + + + + +>Name +>Value + + +<b>USB Series A Hole Mounted</b> + + + + + + + + + + + + + + + + + + + + + +>NAME +PCB Edge + + + + +<b>USB Series A Surface Mounted</b> + + + + + + + + + + + + + + + + +>NAME + + +<b>USB Series B Hole Mounted</b> + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>USB Series Mini-B Surface Mounted</b> + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>USB Series B Hole Mounted</b> + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + +>NAME + + + + + + + + + + + + + + + + + + +>NAME + + +Micro USB Package + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + +USB + + + + + + + + +<b>USB Connectors</b> +<p>USB-B-PTH is fully proven SKU : PRT-00139 +<p>USB-miniB is fully proven SKU : PRT-00587 +<p>USB-A-PCB is untested. +<p>USB-A-H is throughly reviewed, but untested. Spark Fun Electronics SKU : PRT-00437 +<p>USB-B-SMT is throughly reviewed, but untested. Needs silkscreen touching up. +<p>USB-A-S has not been used/tested +<p>USB-MB-H has not been used/tested + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>VALUE + + + + + +>VALUE + + + + + +<b>SUPPLY SYMBOL</b> + + + + + + + + + + + + +<b>SUPPLY SYMBOL</b> + + + + + + + + + + + + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +type V234, grid 12.5 mm + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type V235, grid 17.78 mm + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type V526-0, grid 2.5 mm + + + + + + + + + + +>NAME +>VALUE + + +<b>Mini MELF 0102 Axial</b> + + + + +>NAME +>VALUE + + + +<b>CECC Size RC2211</b> Reflow Soldering<p> +source Beyschlag + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC2211</b> Wave Soldering<p> +source Beyschlag + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type RDH, grid 15 mm + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +RDH + + + + +<b>RESISTOR</b> chip<p> +Source: http://www.vishay.com/docs/20008/dcrcw.pdf + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +Pad definition corrected 2006.05.15, librarian@cadsoft.de + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 2.4 x 4.4 mm + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 2.5 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 3 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 4 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 5 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 6 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm + 5 mm, outline 2.4 x 7 mm + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 2.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 3.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 4.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 5.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 2.4 x 4.4 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 2.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 4.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 3 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 5.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 7.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +Horizontal, grid 5 mm, outline 7.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 3.2 x 10.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 4.2 x 10.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 5.2 x 10.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 4.3 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 5.4 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 6.4 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm + 15.2 mm, outline 6.2 x 18.4 mm + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 5.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 6.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 7.2 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 8.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 9.1 x 18.2 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 6.2 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 7.4 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 8.7 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 10.8 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 11.3 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 9.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 11.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 13.4 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 20.5 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 13.7 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 16.2 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 18.2 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 19.2 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 20.3 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 3.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 15.5 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 6.3 x 10.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 15.4 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 17.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>Ceramic Chip Capacitor KEMET 0204 Reflow solder</b><p> +Metric Code Size 1005 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0603 Reflow solder</b><p> +Metric Code Size 1608 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0805 Reflow solder</b><p> +Metric Code Size 2012 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1206 Reflow solder</b><p> +Metric Code Size 3216 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1210 Reflow solder</b><p> +Metric Code Size 3225 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1812 Reflow solder</b><p> +Metric Code Size 4532 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1825 Reflow solder</b><p> +Metric Code Size 4564 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 2220 Reflow solder</b><p> +Metric Code Size 5650 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 2225 Reflow solder</b><p> +Metric Code Size 5664 + + + + +>NAME +>VALUE + + + + +<b> </b><p> +Source: http://www.vishay.com/docs/10129/hpc0201a.pdf + + +>NAME +>VALUE + + + +Source: http://www.avxcorp.com/docs/catalogs/cx5r.pdf + + +>NAME +>VALUE + + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> wave soldering<p> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +Source: http://download.siliconexpert.com/pdfs/2005/02/24/Semi_Ap/2/VSH/Resistor/dcrcwfre.pdf + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> wave soldering<p> +Source: http://download.siliconexpert.com/pdfs/2005/02/24/Semi_Ap/2/VSH/Resistor/dcrcwfre.pdf + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.10 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.12 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.10 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.12 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +type 0204, grid 5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0204, grid 7.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0204, grid 2.5 mm + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type 0207, grid 10 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0207, grid 12 mm + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<b>RESISTOR</b><p> +type 0207, grid 15mm + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<b>RESISTOR</b><p> +type 0207, grid 2.5 mm + + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type 0207, grid 5 mm + + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type 0207, grid 7.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0309, grid 10mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0309, grid 12.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0309, grid 2.5 mm + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +type 0411, grid 12.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0411, grid 15 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0411, grid 3.81 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0414, grid 15 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0414, grid 5 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0617, grid 17.5 mm + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0617, grid 22.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0617, grid 5 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0922, grid 22.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<b>RESISTOR</b><p> +type 0613, grid 5 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0613, grid 15 mm + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0817, grid 22.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +0817 + + + + +<b>RESISTOR</b><p> +type 0817, grid 6.35 mm + + + + + + +>NAME +>VALUE +0817 + + + +<b>CECC Size RC3715</b> Reflow Soldering<p> +source Beyschlag + + + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC3715</b> Wave Soldering<p> +source Beyschlag + + + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC6123</b> Reflow Soldering<p> +source Beyschlag + + + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC6123</b> Wave Soldering<p> +source Beyschlag + + + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type 0922, grid 7.5 mm + + + + + + +>NAME +>VALUE +0922 + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RBR52<br> +Source: VISHAY .. vta56.pdf + + + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RBR53<br> +Source: VISHAY .. vta56.pdf + + + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RBR54<br> +Source: VISHAY .. vta56.pdf + + + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RBR55<br> +Source: VISHAY .. vta56.pdf + + + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RBR56<br> +Source: VISHAY .. vta56.pdf + + + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RNC55<br> +Source: VISHAY .. vta56.pdf + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RNC60<br> +Source: VISHAY .. vta56.pdf + + + + + + + + +>NAME +>VALUE + + + + +<b>Package 4527</b><p> +Source: http://www.vishay.com/docs/31059/wsrhigh.pdf + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + +>NAME +>VALUE + + +<b>CRCW1218 Thick Film, Rectangular Chip Resistors</b><p> +Source: http://www.vishay.com .. dcrcw.pdf + + + + +>NAME +>VALUE + + + + +<b>Chip Monolithic Ceramic Capacitors</b> Medium Voltage High Capacitance for General Use<p> +Source: http://www.murata.com .. GRM43DR72E224KW01.pdf + + + + + + +>NAME +>VALUE + + + + + + + + +>NAME +>VALUE + + + + + + + + + + +>NAME +>VALUE + + + + + + +<B>CAPACITOR</B>, European symbol + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<B>RESISTOR</B>, European symbol + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>Name +>Value + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + +<b>Resonator</b> +Small SMD resonator. This is the itty bitty 10/20MHz resonators with built in caps. CSTCE10M0G55 and CSTCE20M0V53. Footprint has been reviewed closely but hasn't been tested yet. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 2.4 x 4.4 mm + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 2.5 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 3 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 4 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 5 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 6 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm + 5 mm, outline 2.4 x 7 mm + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 2.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 3.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 4.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 5.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 2.4 x 4.4 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 2.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 4.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 3 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 5.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 7.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +Horizontal, grid 5 mm, outline 7.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 3.2 x 10.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 4.2 x 10.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 5.2 x 10.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 4.3 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 5.4 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 6.4 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm + 15.2 mm, outline 6.2 x 18.4 mm + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 5.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 6.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 7.2 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 8.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 9.1 x 18.2 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 6.2 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 7.4 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 8.7 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 10.8 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 11.3 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 9.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 11.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 13.4 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 20.5 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 13.7 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 16.2 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 18.2 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 19.2 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 20.3 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 3.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 15.5 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 6.3 x 10.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 15.4 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 17.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>Ceramic Chip Capacitor KEMET 0204 Reflow solder</b><p> +Metric Code Size 1005 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0603 Reflow solder</b><p> +Metric Code Size 1608 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0805 Reflow solder</b><p> +Metric Code Size 2012 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1206 Reflow solder</b><p> +Metric Code Size 3216 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1210 Reflow solder</b><p> +Metric Code Size 3225 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1812 Reflow solder</b><p> +Metric Code Size 4532 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1825 Reflow solder</b><p> +Metric Code Size 4564 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 2220 Reflow solder</b><p> +Metric Code Size 5650 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 2225 Reflow solder</b><p> +Metric Code Size 5664 + + + + +>NAME +>VALUE + + + + + + + + +>NAME +>VALUE + + + + + + + + +<B>CAPACITOR</B>, European symbol + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>VOLTAGE REGULATOR</b> + + + + + + + + + +>NAME +>VALUE + + +<b>TO-92</b> + + + + + + + + + + + + +>NAME +>VALUE + + +<b>SOT-23</b> + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + +>NAME +>VALUE +GND +IN +OUT + + + + + + + +SMT 6mm switch, EVQQ2 series +<p>http://www.ladyada.net/library/eagle</p> + + + + + + + + + + + + + + + + + + +<b>VOLTAGE REGULATOR</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>AVR Devices</b><p> +Configurable logic, microcontrollers, nonvolatile memories<p> +Based on the following sources:<p> +<ul> +<li>www.atmel.com +<li>CD-ROM : Configurable Logic Microcontroller Nonvolatile Memory +<li>CadSoft download site, www.cadsoft.de or www.cadsoftusa.com , file at90smcu_v400.zip +<li>avr.lbr +</ul> +<author>Revised by librarian@cadsoft.de</author> + + +<b>32M1-A</b> Micro Lead Frame package (MLF) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<B>Thin Plasic Quad Flat Package</B> Grid 0.8 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>MICROCONTROLLER</b><p> +8 Kbytes FLASH, 1 kbytes SRAM, 512 bytes EEPROM, USART, 6-channel 10 bit ADC, 2-channel 8 bit ADC<br> +Pin compatible with Atmega48, ATMega88, ATMega168<br> +Source: avr.lbr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>Linear Devices</b><p> +Operational amplifiers, comparators, voltage regulators, ADCs, DACs, etc.<p> +<author>Created by librarian@cadsoft.de</author> + + +<b>SMALL OUTLINE TRANSISTOR</b><p> +reflow soldering + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +GND +>NAME +>VALUE + + + + + + + +<b>SOT-23 Single-Supply Centigrade Temperature Sensor</b><p> +Vout = (10mV/°C x Temp °C) +5mmmV<br> + +Source: http://cache.national.com/ds/LM/LM50.pdf + + + + + + + + + + + + + + + + + + + +<b>Pin Header Connectors</b><p> +<author>Created by librarian@cadsoft.de</author> + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +D+ +D- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schematic/partlist.txt b/schematic/partlist.txt new file mode 100644 index 0000000..add4dbe --- /dev/null +++ b/schematic/partlist.txt @@ -0,0 +1,13 @@ +ATmega328p x1 (either DIP or SMT package) +MCP1700-33 x1 3.3V linear regulator +10uF cap x2 +0.1uf cap x2 +47 ohm resistor x2 +1.5k resistor x1 +10k resistor x1 +12MHz crystal x1 (or resonator) +18pf cap x2 (cap for crystal. not needed if you use resonator) +button x1 (for bootloading) +USB cable or connector x1 + +(optional) MCP9700 temperature sensor or any other analog sensor for testing.