aboutsummaryrefslogtreecommitdiffstats
path: root/www/bugmenot-firefox
Commit message (Expand)AuthorAgeFilesLines
* Remove non staged ports without pending PR from wwwbapt2014-09-024-45/+0
* Trying to fix it, but mark it as IGNORE, because ATM it is uncompatiblethierry2014-01-243-10/+21
* Add NO_STAGE all over the place in preparation for the staging support (cat: ...bapt2013-09-211-5/+2
* - Get Rid MD5 supportmiwi2011-03-191-1/+0
* Upgrade to 1.3, switch to the new XPI mechanism, and unbreak.thierry2006-04-174-7/+25
* Mark BROKEN with new Firefox.marcus2005-12-291-0/+2
* Add bugmenot-firefox , a firefox extension to bypass compulsory webthierry2005-05-261-0/+18
Godeps/_workspace/src/github.com/gizak/termui/README.md159
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/bar.go135
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/block.go142
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/block_test.go46
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/box.go117
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/box_others.go14
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/box_windows.go14
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/canvas.go74
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/canvas_test.go55
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/chart.go336
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/chart_others.go11
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/chart_windows.go11
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/doc.go27
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/events.go219
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/events_test.go28
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/barchart.go35
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/barchart.pngbin0 -> 15386 bytes-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gifbin0 -> 453764 bytes-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.go148
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/gauge.go62
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/gauge.pngbin0 -> 32431 bytes-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/grid.gifbin0 -> 800288 bytes-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/grid.go134
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/linechart.go68
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/linechart.pngbin0 -> 139361 bytes-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/list.go41
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/list.pngbin0 -> 37227 bytes-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.go50
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.pngbin0 -> 20075 bytes-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/par.go48
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/par.pngbin0 -> 65773 bytes-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.go65
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.pngbin0 -> 43431 bytes-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/theme.go143
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.pngbin0 -> 105508 bytes-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.pngbin0 -> 90111 bytes-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/gauge.go113
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/grid.go279
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/grid_test.go98
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/helper.go66
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/helper_test.go58
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/list.go104
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/mbar.go233
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/p.go71
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/point.go28
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/render.go60
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/sparkline.go156
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/theme.go84
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/.gitignore5
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/AUTHORS28
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/CHANGELOG.md160
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/CONTRIBUTING.md7
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/LICENSE28
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/README.md92
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/example_test.go34
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify.go111
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_bsd.go496
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_linux.go304
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_bsd.go11
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_darwin.go11
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_symlink_test.go74
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_test.go1010
-rw-r--r--Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_windows.go598
-rw-r--r--Godeps/_workspace/src/github.com/mattn/go-runewidth/.travis.yml9
-rw-r--r--Godeps/_workspace/src/github.com/mattn/go-runewidth/README.mkd25
-rw-r--r--Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth.go404
-rw-r--r--Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_js.go8
-rw-r--r--Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_posix.go69
-rw-r--r--Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_test.go134
-rw-r--r--Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_windows.go24
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/AUTHORS4
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/LICENSE19
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/README.md21
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/api.go451
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/api_common.go183
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/api_windows.go235
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/collect_terminfo.py110
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/syscalls.go39
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/syscalls_darwin_386.go39
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/syscalls_darwin_amd64.go40
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/syscalls_freebsd.go39
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/syscalls_linux.go33
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/syscalls_netbsd.go39
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/syscalls_openbsd.go39
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/syscalls_windows.go61
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/termbox.go407
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/termbox_common.go59
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/termbox_windows.go813
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/terminfo.go219
-rw-r--r--Godeps/_workspace/src/github.com/nsf/termbox-go/terminfo_builtin.go64
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/.gitignore5
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/LICENSE185
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/README.md157
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/all.cpp12
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/bridge.go681
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cdata/cdata.go6
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cdata/cdata12.c18
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cdata/cdata14_386.s17
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cdata/cdata14_amd64.s17
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cdata/cdata14_arm.s18
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cdata/cdata_test.go42
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cmd/genqrc/main.go218
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cmd/ubuntu-touch/particle.desktop15
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cmd/ubuntu-touch/setup.sh41
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/capi.cpp884
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/capi.h211
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/connector.cpp46
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/connector.h58
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/govalue.cpp236
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/govalue.h56
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/govaluetype.cpp254
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/govaluetype.h48
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/idletimer.cpp58
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/mmemwin.cpp27
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/moc_all.cpp4
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/moc_connector.cpp211
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/moc_govalue.cpp155
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/moc_idletimer.cpp108
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/private/qmetaobject_p.h2
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/private/qmetaobjectbuilder_p.h2
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/private/qobject_p.h2
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/private/qtheader.h70
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpp/update-moc.sh19
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpptest/cpptest.cpp14
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpptest/cpptest.go30
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpptest/cpptest.h23
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpptest/moc_testtype.cpp202
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpptest/testtype.h45
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/cpptest/update-moc.sh12
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/datatype.go531
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/doc.go199
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/.gitignore5
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/README.md8
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/basiclayouts/basiclayouts.go29
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/basiclayouts/main.qml116
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/content/AboutDialog.qml48
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/content/ChildWindow.qml122
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/content/Controls.qml229
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/content/ImageViewer.qml58
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/content/Layouts.qml107
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/content/ModelView.qml103
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/content/Styles.qml387
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/gallery.go29
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/bubble.pngbin214 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/button-pressed.pngbin3094 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/button.pngbin3164 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/document-open.pngbin1550 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/document-open@2x.pngbin3355 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/document-save-as.pngbin1837 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/document-save-as@2x.pngbin4500 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/folder_new.pngbin1199 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/go-next.pngbin1219 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/go-previous.pngbin1200 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/preferences-system.pngbin2129 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/process-stop.pngbin1927 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/progress-background.pngbin456 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/progress-fill.pngbin507 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/slider-handle.pngbin3523 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/tab.pngbin9877 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/tab_selected.pngbin10184 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/textfield.pngbin3023 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/toplevel_window.pngbin3690 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/view-refresh.pngbin2024 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/window-new.pngbin671 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/images/window-new@2x.pngbin1900 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/gallery/main.qml266
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/splitview/main.qml82
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/splitview/splitview.go29
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/tableview/images/header.pngbin356 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/tableview/images/selectedrow.pngbin303 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/tableview/main.qml405
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/tableview/tableview.go29
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/content/AndroidDelegate.qml92
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/content/ButtonPage.qml176
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/content/ListPage.qml82
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/content/ProgressBarPage.qml114
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/content/SliderPage.qml106
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/content/TabBarPage.qml102
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/content/TextInputPage.qml106
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/images/NOTICE.txt2
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/images/button_default.pngbin1406 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/images/button_pressed.pngbin1694 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/images/navigation_next_item.pngbin1341 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/images/navigation_previous_item.pngbin1343 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/images/tab_selected.pngbin217 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/images/tabs_standard.pngbin1230 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/images/textinput.pngbin4132 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/images/toolbar.pngbin1643 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/main.qml147
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/controls/touch/touch.go29
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/customtype/customtype.go48
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/customtype/customtype.qml5
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/gopher/gopher.go117
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/gopher/gopher.qml42
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/gopher/mix.qml68
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/gopher/model/README.md10
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/gopher/model/gopher.blendbin1370816 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/gopher/model/gopher.mtl65
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/gopher/model/gopher.obj31375
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/gopher/wavefront.go280
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/imgprovider/imgprovider.go43
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/imgprovider/imgprovider.qml5
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/imgprovider/ubuntu-gopher.pngbin59635 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/modelview/delegate/delegate.go53
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/modelview/delegate/delegate.qml17
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/painting-es2/painting.go115
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/painting-es2/painting.qml66
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/painting/painting.go64
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/painting/painting.qml66
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/particle/main.go84
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/particle/particle.pngbin861 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/particle/particle.qml89
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/qmlscene/Cell.qml20
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/qmlscene/qmlscene.go33
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/qmlscene/tutorial1.qml17
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/qmlscene/tutorial2.qml30
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/qmlscene/tutorial3.qml45
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/qrcpacking/assets/particle.pngbin861 -> 0 bytes-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/qrcpacking/assets/particle.qml89
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/qrcpacking/main.go86
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/qrcpacking/qrc.go58
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/reparent/base.qml7
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/reparent/rect.qml8
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/reparent/reparent.go37
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/examples/snapweb/snapweb.go70
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.0/funcs.cpp1848
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.0/funcs.h347
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.0/gl.go2528
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.1/funcs.cpp2022
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.1/funcs.h376
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.1/gl.go2789
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.2/funcs.cpp2250
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.2/funcs.h414
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.2/gl.go3152
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.3/funcs.cpp2526
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.3/funcs.h460
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.3/gl.go3571
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.4/funcs.cpp2790
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.4/funcs.h504
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.4/gl.go3892
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.5/funcs.cpp2892
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.5/funcs.h521
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/1.5/gl.go4237
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/2.0/funcs.cpp3444
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/2.0/funcs.h613
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/2.0/gl.go6407
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/2.1/funcs.cpp3480
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/2.1/funcs.h619
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/2.1/gl.go6652
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.0/funcs.cpp3966
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.0/funcs.h700
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.0/gl.go7663
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.1/funcs.cpp1422
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.1/funcs.h276
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.1/gl.go4999
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.2compat/funcs.cpp4140
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.2compat/funcs.h729
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.2compat/gl.go7973
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.2core/funcs.cpp1530
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.2core/funcs.h294
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.2core/gl.go4729
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.3compat/funcs.cpp4488
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.3compat/funcs.h787
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.3compat/gl.go8281
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.3core/funcs.cpp1878
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.3core/funcs.h352
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/3.3core/gl.go5489
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.0compat/funcs.cpp4764
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.0compat/funcs.h833
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.0compat/gl.go8607
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.0core/funcs.cpp2154
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.0core/funcs.h398
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.0core/gl.go5815
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.1compat/funcs.cpp5286
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.1compat/funcs.h920
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.1compat/gl.go9201
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.1core/funcs.cpp2676
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.1core/funcs.h485
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.1core/gl.go6409
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.2compat/funcs.cpp5358
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.2compat/funcs.h932
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.2compat/gl.go9386
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.2core/funcs.cpp2748
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.2core/funcs.h497
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.2core/gl.go6594
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.3compat/funcs.cpp5556
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.3compat/funcs.h965
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.3compat/gl.go9845
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.3core/funcs.cpp2946
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.3core/funcs.h530
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/4.3core/gl.go7052
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/es2/funcs.cpp813
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/es2/funcs.h182
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/es2/gl.go2990
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/gengl/Makefile9
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/gengl/funcs.go1764
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/gengl/gl.xml43891
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/gengl/main.go1283
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/gengl/parseqt.go13904
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/gengl/parseqt.rl184
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/gl/glbase/glbase.go33
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/log.go157
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/qml.go1109
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/qml_test.go1436
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/resources.go375
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/stats.go68
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/testing.go69
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/.gitignore9
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/LICENSE29
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/README.md104
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/cmd/metrics-bench/metrics-bench.go20
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/cmd/metrics-example/metrics-example.go154
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/cmd/never-read/never-read.go22
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/counter.go112
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/counter_test.go77
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/debug.go76
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/debug_test.go48
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/ewma.go118
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/ewma_test.go225
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/gauge.go84
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/gauge_float64.go91
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/gauge_float64_test.go38
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/gauge_test.go37
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/graphite.go111
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/graphite_test.go22
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/healthcheck.go61
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/histogram.go202
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/histogram_test.go95
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/influxdb/influxdb.go114
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/json.go83
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/json_test.go28
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/librato/client.go102
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/librato/librato.go230
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/log.go70
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/memory.md285
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/meter.go233
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/meter_test.go60
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/metrics.go13
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/metrics_test.go107
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/opentsdb.go119
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/opentsdb_test.go22
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/registry.go180
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/registry_test.go118
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/runtime.go200
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/runtime_cgo.go10
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/runtime_no_cgo.go7
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/runtime_test.go78
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/sample.go609
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/sample_test.go363
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/stathat/stathat.go69
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/syslog.go78
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/timer.go311
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/timer_test.go81
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/writer.go100
-rw-r--r--Godeps/_workspace/src/github.com/rcrowley/go-metrics/writer_test.go22
-rw-r--r--Godeps/_workspace/src/gopkg.in/qml.v1/cdata/cdata.go6
-rw-r--r--Godeps/_workspace/src/gopkg.in/qml.v1/cdata/cdata12.c18
-rw-r--r--Godeps/_workspace/src/gopkg.in/qml.v1/cdata/cdata14_386.s17
-rw-r--r--Godeps/_workspace/src/gopkg.in/qml.v1/cdata/cdata14_amd64.s17
-rw-r--r--Godeps/_workspace/src/gopkg.in/qml.v1/cdata/cdata14_arm.s18
-rw-r--r--Godeps/_workspace/src/gopkg.in/qml.v1/cdata/cdata_test.go42
-rw-r--r--Godeps/_workspace/src/gopkg.in/qml.v1/gl/glbase/glbase.go33
-rw-r--r--cmd/geth/main.go25
-rw-r--r--cmd/geth/monitorcmd.go309
-rw-r--r--core/chain_manager.go6
-rw-r--r--eth/backend.go22
-rw-r--r--eth/fetcher/fetcher.go19
-rw-r--r--ethdb/database.go48
-rw-r--r--p2p/dial.go4
-rw-r--r--p2p/metrics.go49
-rw-r--r--p2p/server.go6
-rw-r--r--rpc/api/debug.go100
-rw-r--r--rpc/api/debug_args.go24
-rw-r--r--rpc/api/debug_js.go7
-rw-r--r--rpc/xeth.go52
379 files changed, 13272 insertions, 330456 deletions
diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index 6d68c18ec..ce4e8753a 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -25,9 +25,8 @@
"Rev": "8f6ccaaef9b418553807a73a95cb5f49cd3ea39f"
},
{
- "ImportPath": "github.com/howeyc/fsnotify",
- "Comment": "v0.9.0-11-g6b1ef89",
- "Rev": "6b1ef893dc11e0447abda6da20a5203481878dda"
+ "ImportPath": "github.com/gizak/termui",
+ "Rev": "bab8dce01c193d82bc04888a0a9a7814d505f532"
},
{
"ImportPath": "github.com/huin/goupnp",
@@ -50,8 +49,13 @@
"Rev": "fdbe02a1b44e75977b2690062b83cf507d70c013"
},
{
- "ImportPath": "github.com/obscuren/qml",
- "Rev": "c288002b52e905973b131089a8a7c761d4a2c36a"
+ "ImportPath": "github.com/mattn/go-runewidth",
+ "Comment": "travisish-33-g5890272",
+ "Rev": "5890272cd41c5103531cd7b79e428d99c9e97f76"
+ },
+ {
+ "ImportPath": "github.com/nsf/termbox-go",
+ "Rev": "675ffd907b7401b8a709a5ef2249978af5616bb2"
},
{
"ImportPath": "github.com/peterh/liner",
@@ -66,6 +70,10 @@
"Rev": "907cca0f578a5316fb864ec6992dc3d9730ec58c"
},
{
+ "ImportPath": "github.com/rcrowley/go-metrics",
+ "Rev": "a5cfc242a56ba7fa70b785f678d6214837bf93b9"
+ },
+ {
"ImportPath": "github.com/robertkrimen/otto",
"Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba"
},
@@ -117,14 +125,6 @@
{
"ImportPath": "gopkg.in/karalabe/cookiejar.v2/collections/prque",
"Rev": "0b2e270613f5d7ba262a5749b9e32270131497a2"
- },
- {
- "ImportPath": "gopkg.in/qml.v1/cdata",
- "Rev": "1116cb9cd8dee23f8d444ded354eb53122739f99"
- },
- {
- "ImportPath": "gopkg.in/qml.v1/gl/glbase",
- "Rev": "1116cb9cd8dee23f8d444ded354eb53122739f99"
}
]
}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/.gitignore b/Godeps/_workspace/src/github.com/gizak/termui/.gitignore
new file mode 100644
index 000000000..daf913b1b
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/.travis.yml b/Godeps/_workspace/src/github.com/gizak/termui/.travis.yml
new file mode 100644
index 000000000..206e88740
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/.travis.yml
@@ -0,0 +1,6 @@
+language: go
+
+go:
+ - tip
+
+script: go test -v ./ \ No newline at end of file
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/LICENSE b/Godeps/_workspace/src/github.com/gizak/termui/LICENSE
new file mode 100644
index 000000000..311ccc74f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Zack Guo
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/README.md b/Godeps/_workspace/src/github.com/gizak/termui/README.md
new file mode 100644
index 000000000..b9bc3024d
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/README.md
@@ -0,0 +1,159 @@
+# termui [![Build Status](https://travis-ci.org/gizak/termui.svg?branch=master)](https://travis-ci.org/gizak/termui) [![Doc Status](https://godoc.org/github.com/gizak/termui?status.png)](https://godoc.org/github.com/gizak/termui)
+
+## Update 23/06/2015
+Pull requests and master branch are freezing, waiting for merging from `refactoring` branch.
+
+## Notice
+termui comes with ABSOLUTELY NO WARRANTY, and there is a breaking change coming up (see refactoring branch) which will change the `Bufferer` interface and many others. These changes reduce calculation overhead and introduce a new drawing buffer with better capacibilities. We will step into the next stage (call it beta) after merging these changes.
+
+## Introduction
+Go terminal dashboard. Inspired by [blessed-contrib](https://github.com/yaronn/blessed-contrib), but purely in Go.
+
+Cross-platform, easy to compile, and fully-customizable.
+
+__Demo:__ (cast under osx 10.10; Terminal.app; Menlo Regular 12pt.)
+
+<img src="./example/dashboard.gif" alt="demo" width="600">
+
+__Grid layout:__
+
+Expressive syntax, using [12 columns grid system](http://www.w3schools.com/bootstrap/bootstrap_grid_system.asp)
+```go
+ import ui "github.com/gizak/termui"
+ // init and create widgets...
+
+ // build
+ ui.Body.AddRows(
+ ui.NewRow(
+ ui.NewCol(6, 0, widget0),
+ ui.NewCol(6, 0, widget1)),
+ ui.NewRow(
+ ui.NewCol(3, 0, widget2),
+ ui.NewCol(3, 0, widget30, widget31, widget32),
+ ui.NewCol(6, 0, widget4)))
+
+ // calculate layout
+ ui.Body.Align()
+
+ ui.Render(ui.Body)
+```
+[demo code:](https://github.com/gizak/termui/blob/master/example/grid.go)
+
+<img src="./example/grid.gif" alt="grid" width="500">
+
+## Installation
+
+ go get github.com/gizak/termui
+
+## Usage
+
+Each component's layout is a bit like HTML block (box model), which has border and padding.
+
+The `Border` property can be chosen to hide or display (with its border label), when it comes to display, the label takes 1 padding space (i.e. in css: `padding: 1;`, innerHeight and innerWidth therefore shrunk by 1).
+
+`````go
+ import ui "github.com/gizak/termui" // <- ui shortcut, optional
+
+ func main() {
+ err := ui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer ui.Close()
+
+ p := ui.NewPar(":PRESS q TO QUIT DEMO")
+ p.Height = 3
+ p.Width = 50
+ p.TextFgColor = ui.ColorWhite
+ p.Border.Label = "Text Box"
+ p.Border.FgColor = ui.ColorCyan
+
+ g := ui.NewGauge()
+ g.Percent = 50
+ g.Width = 50
+ g.Height = 3
+ g.Y = 11
+ g.Border.Label = "Gauge"
+ g.BarColor = ui.ColorRed
+ g.Border.FgColor = ui.ColorWhite
+ g.Border.LabelFgColor = ui.ColorCyan
+
+ ui.Render(p, g)
+
+ // event handler...
+ }
+`````
+
+Note that components can be overlapped (I'd rather call this a feature...), `Render(rs ...Renderer)` renders its args from left to right (i.e. each component's weight is arising from left to right).
+
+## Themes
+
+_All_ colors in _all_ components can be changed at _any_ time, while there provides some predefined color schemes:
+
+```go
+// for now there are only two themes: default and helloworld
+termui.UseTheme("helloworld")
+
+// create components...
+```
+The `default ` theme's settings depend on the user's terminal color scheme, which is saying if your terminal default font color is white and background is white, it will be like:
+
+<img src="./example/themedefault.png" alt="default" type="image/png" width="600">
+
+The `helloworld` color scheme drops in some colors!
+
+<img src="./example/themehelloworld.png" alt="helloworld" type="image/png" width="600">
+
+## Widgets
+
+#### Par
+
+[demo code](https://github.com/gizak/termui/blob/master/example/par.go)
+
+<img src="./example/par.png" alt="par" type="image/png" width="300">
+
+#### List
+[demo code](https://github.com/gizak/termui/blob/master/example/list.go)
+
+<img src="./example/list.png" alt="list" type="image/png" width="200">
+
+#### Gauge
+[demo code](https://github.com/gizak/termui/blob/master/example/gauge.go)
+
+<img src="./example/gauge.png" alt="gauge" type="image/png" width="350">
+
+#### Line Chart
+[demo code](https://github.com/gizak/termui/blob/master/example/linechart.go)
+
+<img src="./example/linechart.png" alt="linechart" type="image/png" width="450">
+
+#### Bar Chart
+[demo code](https://github.com/gizak/termui/blob/master/example/barchart.go)
+
+<img src="./example/barchart.png" alt="barchart" type="image/png" width="150">
+
+#### Mult-Bar / Stacked-Bar Chart
+[demo code](https://github.com/gizak/termui/blob/master/example/mbarchart.go)
+
+<img src="./example/mbarchart.png" alt="barchart" type="image/png" width="150">
+
+#### Sparklines
+[demo code](https://github.com/gizak/termui/blob/master/example/sparklines.go)
+
+<img src="./example/sparklines.png" alt="sparklines" type="image/png" width="350">
+
+
+## GoDoc
+
+[godoc](https://godoc.org/github.com/gizak/termui)
+
+## TODO
+
+- [x] Grid layout
+- [ ] Event system
+- [ ] Canvas widget
+- [ ] Refine APIs
+- [ ] Focusable widgets
+
+## License
+This library is under the [MIT License](http://opensource.org/licenses/MIT)
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/bar.go b/Godeps/_workspace/src/github.com/gizak/termui/bar.go
new file mode 100644
index 000000000..57bae0ae8
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/bar.go
@@ -0,0 +1,135 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import "fmt"
+
+// BarChart creates multiple bars in a widget:
+/*
+ bc := termui.NewBarChart()
+ data := []int{3, 2, 5, 3, 9, 5}
+ bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
+ bc.Border.Label = "Bar Chart"
+ bc.Data = data
+ bc.Width = 26
+ bc.Height = 10
+ bc.DataLabels = bclabels
+ bc.TextColor = termui.ColorGreen
+ bc.BarColor = termui.ColorRed
+ bc.NumColor = termui.ColorYellow
+*/
+type BarChart struct {
+ Block
+ BarColor Attribute
+ TextColor Attribute
+ NumColor Attribute
+ Data []int
+ DataLabels []string
+ BarWidth int
+ BarGap int
+ labels [][]rune
+ dataNum [][]rune
+ numBar int
+ scale float64
+ max int
+}
+
+// NewBarChart returns a new *BarChart with current theme.
+func NewBarChart() *BarChart {
+ bc := &BarChart{Block: *NewBlock()}
+ bc.BarColor = theme.BarChartBar
+ bc.NumColor = theme.BarChartNum
+ bc.TextColor = theme.BarChartText
+ bc.BarGap = 1
+ bc.BarWidth = 3
+ return bc
+}
+
+func (bc *BarChart) layout() {
+ bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth)
+ bc.labels = make([][]rune, bc.numBar)
+ bc.dataNum = make([][]rune, len(bc.Data))
+
+ for i := 0; i < bc.numBar && i < len(bc.DataLabels) && i < len(bc.Data); i++ {
+ bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
+ n := bc.Data[i]
+ s := fmt.Sprint(n)
+ bc.dataNum[i] = trimStr2Runes(s, bc.BarWidth)
+ }
+
+ //bc.max = bc.Data[0] // what if Data is nil? Sometimes when bar graph is nill it produces panic with panic: runtime error: index out of range
+ // Asign a negative value to get maxvalue auto-populates
+ if bc.max == 0 {
+ bc.max = -1
+ }
+ for i := 0; i < len(bc.Data); i++ {
+ if bc.max < bc.Data[i] {
+ bc.max = bc.Data[i]
+ }
+ }
+ bc.scale = float64(bc.max) / float64(bc.innerHeight-1)
+}
+
+func (bc *BarChart) SetMax(max int) {
+
+ if max > 0 {
+ bc.max = max
+ }
+}
+
+// Buffer implements Bufferer interface.
+func (bc *BarChart) Buffer() []Point {
+ ps := bc.Block.Buffer()
+ bc.layout()
+
+ for i := 0; i < bc.numBar && i < len(bc.Data) && i < len(bc.DataLabels); i++ {
+ h := int(float64(bc.Data[i]) / bc.scale)
+ oftX := i * (bc.BarWidth + bc.BarGap)
+ // plot bar
+ for j := 0; j < bc.BarWidth; j++ {
+ for k := 0; k < h; k++ {
+ p := Point{}
+ p.Ch = ' '
+ p.Bg = bc.BarColor
+ if bc.BarColor == ColorDefault { // when color is default, space char treated as transparent!
+ p.Bg |= AttrReverse
+ }
+ p.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j
+ p.Y = bc.innerY + bc.innerHeight - 2 - k
+ ps = append(ps, p)
+ }
+ }
+ // plot text
+ for j, k := 0, 0; j < len(bc.labels[i]); j++ {
+ w := charWidth(bc.labels[i][j])
+ p := Point{}
+ p.Ch = bc.labels[i][j]
+ p.Bg = bc.BgColor
+ p.Fg = bc.TextColor
+ p.Y = bc.innerY + bc.innerHeight - 1
+ p.X = bc.innerX + oftX + k
+ ps = append(ps, p)
+ k += w
+ }
+ // plot num
+ for j := 0; j < len(bc.dataNum[i]); j++ {
+ p := Point{}
+ p.Ch = bc.dataNum[i][j]
+ p.Fg = bc.NumColor
+ p.Bg = bc.BarColor
+ if bc.BarColor == ColorDefault { // the same as above
+ p.Bg |= AttrReverse
+ }
+ if h == 0 {
+ p.Bg = bc.BgColor
+ }
+ p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j
+ p.Y = bc.innerY + bc.innerHeight - 2
+ ps = append(ps, p)
+ }
+ }
+
+ return bc.Block.chopOverflow(ps)
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/block.go b/Godeps/_workspace/src/github.com/gizak/termui/block.go
new file mode 100644
index 000000000..953136596
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/block.go
@@ -0,0 +1,142 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+// Block is a base struct for all other upper level widgets,
+// consider it as css: display:block.
+// Normally you do not need to create it manually.
+type Block struct {
+ X int
+ Y int
+ Border labeledBorder
+ IsDisplay bool
+ HasBorder bool
+ BgColor Attribute
+ Width int
+ Height int
+ innerWidth int
+ innerHeight int
+ innerX int
+ innerY int
+ PaddingTop int
+ PaddingBottom int
+ PaddingLeft int
+ PaddingRight int
+}
+
+// NewBlock returns a *Block which inherits styles from current theme.
+func NewBlock() *Block {
+ d := Block{}
+ d.IsDisplay = true
+ d.HasBorder = theme.HasBorder
+ d.Border.BgColor = theme.BorderBg
+ d.Border.FgColor = theme.BorderFg
+ d.Border.LabelBgColor = theme.BorderLabelTextBg
+ d.Border.LabelFgColor = theme.BorderLabelTextFg
+ d.BgColor = theme.BlockBg
+ d.Width = 2
+ d.Height = 2
+ return &d
+}
+
+// compute box model
+func (d *Block) align() {
+ d.innerWidth = d.Width - d.PaddingLeft - d.PaddingRight
+ d.innerHeight = d.Height - d.PaddingTop - d.PaddingBottom
+ d.innerX = d.X + d.PaddingLeft
+ d.innerY = d.Y + d.PaddingTop
+
+ if d.HasBorder {
+ d.innerHeight -= 2
+ d.innerWidth -= 2
+ d.Border.X = d.X
+ d.Border.Y = d.Y
+ d.Border.Width = d.Width
+ d.Border.Height = d.Height
+ d.innerX++
+ d.innerY++
+ }
+
+ if d.innerHeight < 0 {
+ d.innerHeight = 0
+ }
+ if d.innerWidth < 0 {
+ d.innerWidth = 0
+ }
+
+}
+
+// InnerBounds returns the internal bounds of the block after aligning and
+// calculating the padding and border, if any.
+func (d *Block) InnerBounds() (x, y, width, height int) {
+ d.align()
+ return d.innerX, d.innerY, d.innerWidth, d.innerHeight
+}
+
+// Buffer implements Bufferer interface.
+// Draw background and border (if any).
+func (d *Block) Buffer() []Point {
+ d.align()
+
+ ps := []Point{}
+ if !d.IsDisplay {
+ return ps
+ }
+
+ if d.HasBorder {
+ ps = d.Border.Buffer()
+ }
+
+ for i := 0; i < d.innerWidth; i++ {
+ for j := 0; j < d.innerHeight; j++ {
+ p := Point{}
+ p.X = d.X + 1 + i
+ p.Y = d.Y + 1 + j
+ p.Ch = ' '
+ p.Bg = d.BgColor
+ ps = append(ps, p)
+ }
+ }
+ return ps
+}
+
+// GetHeight implements GridBufferer.
+// It returns current height of the block.
+func (d Block) GetHeight() int {
+ return d.Height
+}
+
+// SetX implements GridBufferer interface, which sets block's x position.
+func (d *Block) SetX(x int) {
+ d.X = x
+}
+
+// SetY implements GridBufferer interface, it sets y position for block.
+func (d *Block) SetY(y int) {
+ d.Y = y
+}
+
+// SetWidth implements GridBuffer interface, it sets block's width.
+func (d *Block) SetWidth(w int) {
+ d.Width = w
+}
+
+// chop the overflow parts
+func (d *Block) chopOverflow(ps []Point) []Point {
+ nps := make([]Point, 0, len(ps))
+ x := d.X
+ y := d.Y
+ w := d.Width
+ h := d.Height
+ for _, v := range ps {
+ if v.X >= x &&
+ v.X < x+w &&
+ v.Y >= y &&
+ v.Y < y+h {
+ nps = append(nps, v)
+ }
+ }
+ return nps
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/block_test.go b/Godeps/_workspace/src/github.com/gizak/termui/block_test.go
new file mode 100644
index 000000000..2de205b21
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/block_test.go
@@ -0,0 +1,46 @@
+package termui
+
+import "testing"
+
+func TestBlock_InnerBounds(t *testing.T) {
+ b := NewBlock()
+ b.X = 10
+ b.Y = 11
+ b.Width = 12
+ b.Height = 13
+
+ assert := func(name string, x, y, w, h int) {
+ t.Log(name)
+ cx, cy, cw, ch := b.InnerBounds()
+ if cx != x {
+ t.Errorf("expected x to be %d but got %d", x, cx)
+ }
+ if cy != y {
+ t.Errorf("expected y to be %d but got %d", y, cy)
+ }
+ if cw != w {
+ t.Errorf("expected width to be %d but got %d", w, cw)
+ }
+ if ch != h {
+ t.Errorf("expected height to be %d but got %d", h, ch)
+ }
+ }
+
+ b.HasBorder = false
+ assert("no border, no padding", 10, 11, 12, 13)
+
+ b.HasBorder = true
+ assert("border, no padding", 11, 12, 10, 11)
+
+ b.PaddingBottom = 2
+ assert("border, 2b padding", 11, 12, 10, 9)
+
+ b.PaddingTop = 3
+ assert("border, 2b 3t padding", 11, 15, 10, 6)
+
+ b.PaddingLeft = 4
+ assert("border, 2b 3t 4l padding", 15, 15, 6, 6)
+
+ b.PaddingRight = 5
+ assert("border, 2b 3t 4l 5r padding", 15, 15, 1, 6)
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/box.go b/Godeps/_workspace/src/github.com/gizak/termui/box.go
new file mode 100644
index 000000000..1dcfd8692
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/box.go
@@ -0,0 +1,117 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+type border struct {
+ X int
+ Y int
+ Width int
+ Height int
+ FgColor Attribute
+ BgColor Attribute
+}
+
+type hline struct {
+ X int
+ Y int
+ Length int
+ FgColor Attribute
+ BgColor Attribute
+}
+
+type vline struct {
+ X int
+ Y int
+ Length int
+ FgColor Attribute
+ BgColor Attribute
+}
+
+// Draw a horizontal line.
+func (l hline) Buffer() []Point {
+ pts := make([]Point, l.Length)
+ for i := 0; i < l.Length; i++ {
+ pts[i].X = l.X + i
+ pts[i].Y = l.Y
+ pts[i].Ch = HORIZONTAL_LINE
+ pts[i].Bg = l.BgColor
+ pts[i].Fg = l.FgColor
+ }
+ return pts
+}
+
+// Draw a vertical line.
+func (l vline) Buffer() []Point {
+ pts := make([]Point, l.Length)
+ for i := 0; i < l.Length; i++ {
+ pts[i].X = l.X
+ pts[i].Y = l.Y + i
+ pts[i].Ch = VERTICAL_LINE
+ pts[i].Bg = l.BgColor
+ pts[i].Fg = l.FgColor
+ }
+ return pts
+}
+
+// Draw a box border.
+func (b border) Buffer() []Point {
+ if b.Width < 2 || b.Height < 2 {
+ return nil
+ }
+ pts := make([]Point, 2*b.Width+2*b.Height-4)
+
+ pts[0].X = b.X
+ pts[0].Y = b.Y
+ pts[0].Fg = b.FgColor
+ pts[0].Bg = b.BgColor
+ pts[0].Ch = TOP_LEFT
+
+ pts[1].X = b.X + b.Width - 1
+ pts[1].Y = b.Y
+ pts[1].Fg = b.FgColor
+ pts[1].Bg = b.BgColor
+ pts[1].Ch = TOP_RIGHT
+
+ pts[2].X = b.X
+ pts[2].Y = b.Y + b.Height - 1
+ pts[2].Fg = b.FgColor
+ pts[2].Bg = b.BgColor
+ pts[2].Ch = BOTTOM_LEFT
+
+ pts[3].X = b.X + b.Width - 1
+ pts[3].Y = b.Y + b.Height - 1
+ pts[3].Fg = b.FgColor
+ pts[3].Bg = b.BgColor
+ pts[3].Ch = BOTTOM_RIGHT
+
+ copy(pts[4:], (hline{b.X + 1, b.Y, b.Width - 2, b.FgColor, b.BgColor}).Buffer())
+ copy(pts[4+b.Width-2:], (hline{b.X + 1, b.Y + b.Height - 1, b.Width - 2, b.FgColor, b.BgColor}).Buffer())
+ copy(pts[4+2*b.Width-4:], (vline{b.X, b.Y + 1, b.Height - 2, b.FgColor, b.BgColor}).Buffer())
+ copy(pts[4+2*b.Width-4+b.Height-2:], (vline{b.X + b.Width - 1, b.Y + 1, b.Height - 2, b.FgColor, b.BgColor}).Buffer())
+
+ return pts
+}
+
+type labeledBorder struct {
+ border
+ Label string
+ LabelFgColor Attribute
+ LabelBgColor Attribute
+}
+
+// Draw a box border with label.
+func (lb labeledBorder) Buffer() []Point {
+ ps := lb.border.Buffer()
+ maxTxtW := lb.Width - 2
+ rs := trimStr2Runes(lb.Label, maxTxtW)
+
+ for i, j, w := 0, 0, 0; i < len(rs); i++ {
+ w = charWidth(rs[i])
+ ps = append(ps, newPointWithAttrs(rs[i], lb.X+1+j, lb.Y, lb.LabelFgColor, lb.LabelBgColor))
+ j += w
+ }
+
+ return ps
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/box_others.go b/Godeps/_workspace/src/github.com/gizak/termui/box_others.go
new file mode 100644
index 000000000..bcc3d7ded
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/box_others.go
@@ -0,0 +1,14 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build !windows
+
+package termui
+
+const TOP_RIGHT = '┐'
+const VERTICAL_LINE = '│'
+const HORIZONTAL_LINE = '─'
+const TOP_LEFT = '┌'
+const BOTTOM_RIGHT = '┘'
+const BOTTOM_LEFT = '└'
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/box_windows.go b/Godeps/_workspace/src/github.com/gizak/termui/box_windows.go
new file mode 100644
index 000000000..dd39019fe
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/box_windows.go
@@ -0,0 +1,14 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build windows
+
+package termui
+
+const TOP_RIGHT = '+'
+const VERTICAL_LINE = '|'
+const HORIZONTAL_LINE = '-'
+const TOP_LEFT = '+'
+const BOTTOM_RIGHT = '+'
+const BOTTOM_LEFT = '+'
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/canvas.go b/Godeps/_workspace/src/github.com/gizak/termui/canvas.go
new file mode 100644
index 000000000..614635ee4
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/canvas.go
@@ -0,0 +1,74 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+/*
+dots:
+ ,___,
+ |1 4|
+ |2 5|
+ |3 6|
+ |7 8|
+ `````
+*/
+
+var brailleBase = '\u2800'
+
+var brailleOftMap = [4][2]rune{
+ {'\u0001', '\u0008'},
+ {'\u0002', '\u0010'},
+ {'\u0004', '\u0020'},
+ {'\u0040', '\u0080'}}
+
+// Canvas contains drawing map: i,j -> rune
+type Canvas map[[2]int]rune
+
+// NewCanvas returns an empty Canvas
+func NewCanvas() Canvas {
+ return make(map[[2]int]rune)
+}
+
+func chOft(x, y int) rune {
+ return brailleOftMap[y%4][x%2]
+}
+
+func (c Canvas) rawCh(x, y int) rune {
+ if ch, ok := c[[2]int{x, y}]; ok {
+ return ch
+ }
+ return '\u0000' //brailleOffset
+}
+
+// return coordinate in terminal
+func chPos(x, y int) (int, int) {
+ return y / 4, x / 2
+}
+
+// Set sets a point (x,y) in the virtual coordinate
+func (c Canvas) Set(x, y int) {
+ i, j := chPos(x, y)
+ ch := c.rawCh(i, j)
+ ch |= chOft(x, y)
+ c[[2]int{i, j}] = ch
+}
+
+// Unset removes point (x,y)
+func (c Canvas) Unset(x, y int) {
+ i, j := chPos(x, y)
+ ch := c.rawCh(i, j)
+ ch &= ^chOft(x, y)
+ c[[2]int{i, j}] = ch
+}
+
+// Buffer returns un-styled points
+func (c Canvas) Buffer() []Point {
+ ps := make([]Point, len(c))
+ i := 0
+ for k, v := range c {
+ ps[i] = newPoint(v+brailleBase, k[0], k[1])
+ i++
+ }
+ return ps
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/canvas_test.go b/Godeps/_workspace/src/github.com/gizak/termui/canvas_test.go
new file mode 100644
index 000000000..021949ced
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/canvas_test.go
@@ -0,0 +1,55 @@
+package termui
+
+import (
+ "testing"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+func TestCanvasSet(t *testing.T) {
+ c := NewCanvas()
+ c.Set(0, 0)
+ c.Set(0, 1)
+ c.Set(0, 2)
+ c.Set(0, 3)
+ c.Set(1, 3)
+ c.Set(2, 3)
+ c.Set(3, 3)
+ c.Set(4, 3)
+ c.Set(5, 3)
+ spew.Dump(c)
+}
+
+func TestCanvasUnset(t *testing.T) {
+ c := NewCanvas()
+ c.Set(0, 0)
+ c.Set(0, 1)
+ c.Set(0, 2)
+ c.Unset(0, 2)
+ spew.Dump(c)
+ c.Unset(0, 3)
+ spew.Dump(c)
+}
+
+func TestCanvasBuffer(t *testing.T) {
+ c := NewCanvas()
+ c.Set(0, 0)
+ c.Set(0, 1)
+ c.Set(0, 2)
+ c.Set(0, 3)
+ c.Set(1, 3)
+ c.Set(2, 3)
+ c.Set(3, 3)
+ c.Set(4, 3)
+ c.Set(5, 3)
+ c.Set(6, 3)
+ c.Set(7, 2)
+ c.Set(8, 1)
+ c.Set(9, 0)
+ bufs := c.Buffer()
+ rs := make([]rune, len(bufs))
+ for i, v := range bufs {
+ rs[i] = v.Ch
+ }
+ spew.Dump(string(rs))
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/chart.go b/Godeps/_workspace/src/github.com/gizak/termui/chart.go
new file mode 100644
index 000000000..d6fb8bc7d
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/chart.go
@@ -0,0 +1,336 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "fmt"
+ "math"
+)
+
+// only 16 possible combinations, why bother
+var braillePatterns = map[[2]int]rune{
+ [2]int{0, 0}: '⣀',
+ [2]int{0, 1}: '⡠',
+ [2]int{0, 2}: '⡐',
+ [2]int{0, 3}: '⡈',
+
+ [2]int{1, 0}: '⢄',
+ [2]int{1, 1}: '⠤',
+ [2]int{1, 2}: '⠔',
+ [2]int{1, 3}: '⠌',
+
+ [2]int{2, 0}: '⢂',
+ [2]int{2, 1}: '⠢',
+ [2]int{2, 2}: '⠒',
+ [2]int{2, 3}: '⠊',
+
+ [2]int{3, 0}: '⢁',
+ [2]int{3, 1}: '⠡',
+ [2]int{3, 2}: '⠑',
+ [2]int{3, 3}: '⠉',
+}
+
+var lSingleBraille = [4]rune{'\u2840', '⠄', '⠂', '⠁'}
+var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'}
+
+// LineChart has two modes: braille(default) and dot. Using braille gives 2x capicity as dot mode,
+// because one braille char can represent two data points.
+/*
+ lc := termui.NewLineChart()
+ lc.Border.Label = "braille-mode Line Chart"
+ lc.Data = [1.2, 1.3, 1.5, 1.7, 1.5, 1.6, 1.8, 2.0]
+ lc.Width = 50
+ lc.Height = 12
+ lc.AxesColor = termui.ColorWhite
+ lc.LineColor = termui.ColorGreen | termui.AttrBold
+ // termui.Render(lc)...
+*/
+type LineChart struct {
+ Block
+ Data []float64
+ DataLabels []string // if unset, the data indices will be used
+ Mode string // braille | dot
+ DotStyle rune
+ LineColor Attribute
+ scale float64 // data span per cell on y-axis
+ AxesColor Attribute
+ drawingX int
+ drawingY int
+ axisYHeight int
+ axisXWidth int
+ axisYLebelGap int
+ axisXLebelGap int
+ topValue float64
+ bottomValue float64
+ labelX [][]rune
+ labelY [][]rune
+ labelYSpace int
+ maxY float64
+ minY float64
+}
+
+// NewLineChart returns a new LineChart with current theme.
+func NewLineChart() *LineChart {
+ lc := &LineChart{Block: *NewBlock()}
+ lc.AxesColor = theme.LineChartAxes
+ lc.LineColor = theme.LineChartLine
+ lc.Mode = "braille"
+ lc.DotStyle = '•'
+ lc.axisXLebelGap = 2
+ lc.axisYLebelGap = 1
+ lc.bottomValue = math.Inf(1)
+ lc.topValue = math.Inf(-1)
+ return lc
+}
+
+// one cell contains two data points
+// so the capicity is 2x as dot-mode
+func (lc *LineChart) renderBraille() []Point {
+ ps := []Point{}
+
+ // return: b -> which cell should the point be in
+ // m -> in the cell, divided into 4 equal height levels, which subcell?
+ getPos := func(d float64) (b, m int) {
+ cnt4 := int((d-lc.bottomValue)/(lc.scale/4) + 0.5)
+ b = cnt4 / 4
+ m = cnt4 % 4
+ return
+ }
+ // plot points
+ for i := 0; 2*i+1 < len(lc.Data) && i < lc.axisXWidth; i++ {
+ b0, m0 := getPos(lc.Data[2*i])
+ b1, m1 := getPos(lc.Data[2*i+1])
+
+ if b0 == b1 {
+ p := Point{}
+ p.Ch = braillePatterns[[2]int{m0, m1}]
+ p.Bg = lc.BgColor
+ p.Fg = lc.LineColor
+ p.Y = lc.innerY + lc.innerHeight - 3 - b0
+ p.X = lc.innerX + lc.labelYSpace + 1 + i
+ ps = append(ps, p)
+ } else {
+ p0 := newPointWithAttrs(lSingleBraille[m0],
+ lc.innerX+lc.labelYSpace+1+i,
+ lc.innerY+lc.innerHeight-3-b0,
+ lc.LineColor,
+ lc.BgColor)
+ p1 := newPointWithAttrs(rSingleBraille[m1],
+ lc.innerX+lc.labelYSpace+1+i,
+ lc.innerY+lc.innerHeight-3-b1,
+ lc.LineColor,
+ lc.BgColor)
+ ps = append(ps, p0, p1)
+ }
+
+ }
+ return ps
+}
+
+func (lc *LineChart) renderDot() []Point {
+ ps := []Point{}
+ for i := 0; i < len(lc.Data) && i < lc.axisXWidth; i++ {
+ p := Point{}
+ p.Ch = lc.DotStyle
+ p.Fg = lc.LineColor
+ p.Bg = lc.BgColor
+ p.X = lc.innerX + lc.labelYSpace + 1 + i
+ p.Y = lc.innerY + lc.innerHeight - 3 - int((lc.Data[i]-lc.bottomValue)/lc.scale+0.5)
+ ps = append(ps, p)
+ }
+
+ return ps
+}
+
+func (lc *LineChart) calcLabelX() {
+ lc.labelX = [][]rune{}
+
+ for i, l := 0, 0; i < len(lc.DataLabels) && l < lc.axisXWidth; i++ {
+ if lc.Mode == "dot" {
+ if l >= len(lc.DataLabels) {
+ break
+ }
+
+ s := str2runes(lc.DataLabels[l])
+ w := strWidth(lc.DataLabels[l])
+ if l+w <= lc.axisXWidth {
+ lc.labelX = append(lc.labelX, s)
+ }
+ l += w + lc.axisXLebelGap
+ } else { // braille
+ if 2*l >= len(lc.DataLabels) {
+ break
+ }
+
+ s := str2runes(lc.DataLabels[2*l])
+ w := strWidth(lc.DataLabels[2*l])
+ if l+w <= lc.axisXWidth {
+ lc.labelX = append(lc.labelX, s)
+ }
+ l += w + lc.axisXLebelGap
+
+ }
+ }
+}
+
+func shortenFloatVal(x float64) string {
+ s := fmt.Sprintf("%.2f", x)
+ if len(s)-3 > 3 {
+ s = fmt.Sprintf("%.2e", x)
+ }
+
+ if x < 0 {
+ s = fmt.Sprintf("%.2f", x)
+ }
+ return s
+}
+
+func (lc *LineChart) calcLabelY() {
+ span := lc.topValue - lc.bottomValue
+ lc.scale = span / float64(lc.axisYHeight)
+
+ n := (1 + lc.axisYHeight) / (lc.axisYLebelGap + 1)
+ lc.labelY = make([][]rune, n)
+ maxLen := 0
+ for i := 0; i < n; i++ {
+ s := str2runes(shortenFloatVal(lc.bottomValue + float64(i)*span/float64(n)))
+ if len(s) > maxLen {
+ maxLen = len(s)
+ }
+ lc.labelY[i] = s
+ }
+
+ lc.labelYSpace = maxLen
+}
+
+func (lc *LineChart) calcLayout() {
+ // set datalabels if it is not provided
+ if lc.DataLabels == nil || len(lc.DataLabels) == 0 {
+ lc.DataLabels = make([]string, len(lc.Data))
+ for i := range lc.Data {
+ lc.DataLabels[i] = fmt.Sprint(i)
+ }
+ }
+
+ // lazy increase, to avoid y shaking frequently
+ // update bound Y when drawing is gonna overflow
+ lc.minY = lc.Data[0]
+ lc.maxY = lc.Data[0]
+
+ // valid visible range
+ vrange := lc.innerWidth
+ if lc.Mode == "braille" {
+ vrange = 2 * lc.innerWidth
+ }
+ if vrange > len(lc.Data) {
+ vrange = len(lc.Data)
+ }
+
+ for _, v := range lc.Data[:vrange] {
+ if v > lc.maxY {
+ lc.maxY = v
+ }
+ if v < lc.minY {
+ lc.minY = v
+ }
+ }
+
+ span := lc.maxY - lc.minY
+
+ if lc.minY < lc.bottomValue {
+ lc.bottomValue = lc.minY - 0.2*span
+ }
+
+ if lc.maxY > lc.topValue {
+ lc.topValue = lc.maxY + 0.2*span
+ }
+
+ lc.axisYHeight = lc.innerHeight - 2
+ lc.calcLabelY()
+
+ lc.axisXWidth = lc.innerWidth - 1 - lc.labelYSpace
+ lc.calcLabelX()
+
+ lc.drawingX = lc.innerX + 1 + lc.labelYSpace
+ lc.drawingY = lc.innerY
+}
+
+func (lc *LineChart) plotAxes() []Point {
+ origY := lc.innerY + lc.innerHeight - 2
+ origX := lc.innerX + lc.labelYSpace
+
+ ps := []Point{newPointWithAttrs(ORIGIN, origX, origY, lc.AxesColor, lc.BgColor)}
+
+ for x := origX + 1; x < origX+lc.axisXWidth; x++ {
+ p := Point{}
+ p.X = x
+ p.Y = origY
+ p.Bg = lc.BgColor
+ p.Fg = lc.AxesColor
+ p.Ch = HDASH
+ ps = append(ps, p)
+ }
+
+ for dy := 1; dy <= lc.axisYHeight; dy++ {
+ p := Point{}
+ p.X = origX
+ p.Y = origY - dy
+ p.Bg = lc.BgColor
+ p.Fg = lc.AxesColor
+ p.Ch = VDASH
+ ps = append(ps, p)
+ }
+
+ // x label
+ oft := 0
+ for _, rs := range lc.labelX {
+ if oft+len(rs) > lc.axisXWidth {
+ break
+ }
+ for j, r := range rs {
+ p := Point{}
+ p.Ch = r
+ p.Fg = lc.AxesColor
+ p.Bg = lc.BgColor
+ p.X = origX + oft + j
+ p.Y = lc.innerY + lc.innerHeight - 1
+ ps = append(ps, p)
+ }
+ oft += len(rs) + lc.axisXLebelGap
+ }
+
+ // y labels
+ for i, rs := range lc.labelY {
+ for j, r := range rs {
+ p := Point{}
+ p.Ch = r
+ p.Fg = lc.AxesColor
+ p.Bg = lc.BgColor
+ p.X = lc.innerX + j
+ p.Y = origY - i*(lc.axisYLebelGap+1)
+ ps = append(ps, p)
+ }
+ }
+
+ return ps
+}
+
+// Buffer implements Bufferer interface.
+func (lc *LineChart) Buffer() []Point {
+ ps := lc.Block.Buffer()
+ if lc.Data == nil || len(lc.Data) == 0 {
+ return ps
+ }
+ lc.calcLayout()
+ ps = append(ps, lc.plotAxes()...)
+
+ if lc.Mode == "dot" {
+ ps = append(ps, lc.renderDot()...)
+ } else {
+ ps = append(ps, lc.renderBraille()...)
+ }
+
+ return lc.Block.chopOverflow(ps)
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/chart_others.go b/Godeps/_workspace/src/github.com/gizak/termui/chart_others.go
new file mode 100644
index 000000000..8911873b6
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/chart_others.go
@@ -0,0 +1,11 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build !windows
+
+package termui
+
+const VDASH = '┊'
+const HDASH = '┈'
+const ORIGIN = '└'
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/chart_windows.go b/Godeps/_workspace/src/github.com/gizak/termui/chart_windows.go
new file mode 100644
index 000000000..9f9a5e96c
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/chart_windows.go
@@ -0,0 +1,11 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build windows
+
+package termui
+
+const VDASH = '|'
+const HDASH = '-'
+const ORIGIN = '+'
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/doc.go b/Godeps/_workspace/src/github.com/gizak/termui/doc.go
new file mode 100644
index 000000000..43f886f55
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/doc.go
@@ -0,0 +1,27 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+/*
+Package termui is a library designed for creating command line UI. For more info, goto http://github.com/gizak/termui
+
+A simplest example:
+ package main
+
+ import ui "github.com/gizak/termui"
+
+ func main() {
+ if err:=ui.Init(); err != nil {
+ panic(err)
+ }
+ defer ui.Close()
+
+ g := ui.NewGauge()
+ g.Percent = 50
+ g.Width = 50
+ g.Border.Label = "Gauge"
+
+ ui.Render(g)
+ }
+*/
+package termui
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/events.go b/Godeps/_workspace/src/github.com/gizak/termui/events.go
new file mode 100644
index 000000000..23a189b56
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/events.go
@@ -0,0 +1,219 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+//
+// Portions of this file uses [termbox-go](https://github.com/nsf/termbox-go/blob/54b74d087b7c397c402d0e3b66d2ccb6eaf5c2b4/api_common.go)
+// by [authors](https://github.com/nsf/termbox-go/blob/master/AUTHORS)
+// under [license](https://github.com/nsf/termbox-go/blob/master/LICENSE)
+
+package termui
+
+import "github.com/nsf/termbox-go"
+
+/***********************************termbox-go**************************************/
+
+type (
+ EventType uint8
+ Modifier uint8
+ Key uint16
+)
+
+// This type represents a termbox event. The 'Mod', 'Key' and 'Ch' fields are
+// valid if 'Type' is EventKey. The 'Width' and 'Height' fields are valid if
+// 'Type' is EventResize. The 'Err' field is valid if 'Type' is EventError.
+type Event struct {
+ Type EventType // one of Event* constants
+ Mod Modifier // one of Mod* constants or 0
+ Key Key // one of Key* constants, invalid if 'Ch' is not 0
+ Ch rune // a unicode character
+ Width int // width of the screen
+ Height int // height of the screen
+ Err error // error in case if input failed
+ MouseX int // x coord of mouse
+ MouseY int // y coord of mouse
+ N int // number of bytes written when getting a raw event
+}
+
+const (
+ KeyF1 Key = 0xFFFF - iota
+ KeyF2
+ KeyF3
+ KeyF4
+ KeyF5
+ KeyF6
+ KeyF7
+ KeyF8
+ KeyF9
+ KeyF10
+ KeyF11
+ KeyF12
+ KeyInsert
+ KeyDelete
+ KeyHome
+ KeyEnd
+ KeyPgup
+ KeyPgdn
+ KeyArrowUp
+ KeyArrowDown
+ KeyArrowLeft
+ KeyArrowRight
+ key_min // see terminfo
+ MouseLeft
+ MouseMiddle
+ MouseRight
+)
+
+const (
+ KeyCtrlTilde Key = 0x00
+ KeyCtrl2 Key = 0x00
+ KeyCtrlSpace Key = 0x00
+ KeyCtrlA Key = 0x01
+ KeyCtrlB Key = 0x02
+ KeyCtrlC Key = 0x03
+ KeyCtrlD Key = 0x04
+ KeyCtrlE Key = 0x05
+ KeyCtrlF Key = 0x06
+ KeyCtrlG Key = 0x07
+ KeyBackspace Key = 0x08
+ KeyCtrlH Key = 0x08
+ KeyTab Key = 0x09
+ KeyCtrlI Key = 0x09
+ KeyCtrlJ Key = 0x0A
+ KeyCtrlK Key = 0x0B
+ KeyCtrlL Key = 0x0C
+ KeyEnter Key = 0x0D
+ KeyCtrlM Key = 0x0D
+ KeyCtrlN Key = 0x0E
+ KeyCtrlO Key = 0x0F
+ KeyCtrlP Key = 0x10
+ KeyCtrlQ Key = 0x11
+ KeyCtrlR Key = 0x12
+ KeyCtrlS Key = 0x13
+ KeyCtrlT Key = 0x14
+ KeyCtrlU Key = 0x15
+ KeyCtrlV Key = 0x16
+ KeyCtrlW Key = 0x17
+ KeyCtrlX Key = 0x18
+ KeyCtrlY Key = 0x19
+ KeyCtrlZ Key = 0x1A
+ KeyEsc Key = 0x1B
+ KeyCtrlLsqBracket Key = 0x1B
+ KeyCtrl3 Key = 0x1B
+ KeyCtrl4 Key = 0x1C
+ KeyCtrlBackslash Key = 0x1C
+ KeyCtrl5 Key = 0x1D
+ KeyCtrlRsqBracket Key = 0x1D
+ KeyCtrl6 Key = 0x1E
+ KeyCtrl7 Key = 0x1F
+ KeyCtrlSlash Key = 0x1F
+ KeyCtrlUnderscore Key = 0x1F
+ KeySpace Key = 0x20
+ KeyBackspace2 Key = 0x7F
+ KeyCtrl8 Key = 0x7F
+)
+
+// Alt modifier constant, see Event.Mod field and SetInputMode function.
+const (
+ ModAlt Modifier = 0x01
+)
+
+// Event type. See Event.Type field.
+const (
+ EventKey EventType = iota
+ EventResize
+ EventMouse
+ EventError
+ EventInterrupt
+ EventRaw
+ EventNone
+)
+
+/**************************************end**************************************/
+
+// convert termbox.Event to termui.Event
+func uiEvt(e termbox.Event) Event {
+ event := Event{}
+ event.Type = EventType(e.Type)
+ event.Mod = Modifier(e.Mod)
+ event.Key = Key(e.Key)
+ event.Ch = e.Ch
+ event.Width = e.Width
+ event.Height = e.Height
+ event.Err = e.Err
+ event.MouseX = e.MouseX
+ event.MouseY = e.MouseY
+ event.N = e.N
+
+ return event
+}
+
+var evtChs = make([]chan Event, 0)
+
+// EventCh returns an output-only event channel.
+// This function can be called many times (multiplexer).
+func EventCh() <-chan Event {
+ out := make(chan Event)
+ evtChs = append(evtChs, out)
+ return out
+}
+
+// turn on event listener
+func evtListen() {
+ go func() {
+ for {
+ e := termbox.PollEvent()
+ // dispatch
+ for _, c := range evtChs {
+ go func(ch chan Event) {
+ ch <- uiEvt(e)
+ }(c)
+ }
+ }
+ }()
+}
+
+/*
+// EventHandlers is a handler sequence
+var EventHandlers []func(Event)
+
+var signalQuit = make(chan bool)
+
+// Quit sends quit signal to terminate termui
+func Quit() {
+ signalQuit <- true
+}
+
+// Wait listening to signalQuit, block operation.
+func Wait() {
+ <-signalQuit
+}
+
+// RegEvtHandler register function into TSEventHandler sequence.
+func RegEvtHandler(fn func(Event)) {
+ EventHandlers = append(EventHandlers, fn)
+}
+
+// EventLoop handles all events and
+// redirects every event to callbacks in EventHandlers
+func EventLoop() {
+ evt := make(chan termbox.Event)
+
+ go func() {
+ for {
+ evt <- termbox.PollEvent()
+ }
+ }()
+
+ for {
+ select {
+ case c := <-signalQuit:
+ defer func() { signalQuit <- c }()
+ return
+ case e := <-evt:
+ for _, fn := range EventHandlers {
+ fn(uiEvt(e))
+ }
+ }
+ }
+}
+*/
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/events_test.go b/Godeps/_workspace/src/github.com/gizak/termui/events_test.go
new file mode 100644
index 000000000..1137b1d26
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/events_test.go
@@ -0,0 +1,28 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+//
+// Portions of this file uses [termbox-go](https://github.com/nsf/termbox-go/blob/54b74d087b7c397c402d0e3b66d2ccb6eaf5c2b4/api_common.go)
+// by [authors](https://github.com/nsf/termbox-go/blob/master/AUTHORS)
+// under [license](https://github.com/nsf/termbox-go/blob/master/LICENSE)
+
+package termui
+
+import (
+ "errors"
+ "testing"
+
+ termbox "github.com/nsf/termbox-go"
+ "github.com/stretchr/testify/assert"
+)
+
+type boxEvent termbox.Event
+
+func TestUiEvt(t *testing.T) {
+ err := errors.New("This is a mock error")
+ event := boxEvent{3, 5, 2, 'H', 200, 500, err, 50, 30, 2}
+ expetced := Event{3, 5, 2, 'H', 200, 500, err, 50, 30, 2}
+
+ // We need to do that ugly casting so that vet does not complain
+ assert.Equal(t, uiEvt(termbox.Event(event)), expetced)
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.go b/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.go
new file mode 100644
index 000000000..83947f580
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.go
@@ -0,0 +1,35 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "github.com/gizak/termui"
+
+func main() {
+ err := termui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer termui.Close()
+
+ termui.UseTheme("helloworld")
+
+ bc := termui.NewBarChart()
+ data := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6}
+ bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
+ bc.Border.Label = "Bar Chart"
+ bc.Data = data
+ bc.Width = 26
+ bc.Height = 10
+ bc.DataLabels = bclabels
+ bc.TextColor = termui.ColorGreen
+ bc.BarColor = termui.ColorRed
+ bc.NumColor = termui.ColorYellow
+
+ termui.Render(bc)
+
+ <-termui.EventCh()
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.png b/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.png
new file mode 100644
index 000000000..a37912f7f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif
new file mode 100644
index 000000000..8e1859c72
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.go b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.go
new file mode 100644
index 000000000..c14bb4413
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.go
@@ -0,0 +1,148 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import ui "github.com/gizak/termui"
+import "math"
+
+import "time"
+
+func main() {
+ err := ui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer ui.Close()
+
+ p := ui.NewPar(":PRESS q TO QUIT DEMO")
+ p.Height = 3
+ p.Width = 50
+ p.TextFgColor = ui.ColorWhite
+ p.Border.Label = "Text Box"
+ p.Border.FgColor = ui.ColorCyan
+
+ strs := []string{"[0] gizak/termui", "[1] editbox.go", "[2] iterrupt.go", "[3] keyboard.go", "[4] output.go", "[5] random_out.go", "[6] dashboard.go", "[7] nsf/termbox-go"}
+ list := ui.NewList()
+ list.Items = strs
+ list.ItemFgColor = ui.ColorYellow
+ list.Border.Label = "List"
+ list.Height = 7
+ list.Width = 25
+ list.Y = 4
+
+ g := ui.NewGauge()
+ g.Percent = 50
+ g.Width = 50
+ g.Height = 3
+ g.Y = 11
+ g.Border.Label = "Gauge"
+ g.BarColor = ui.ColorRed
+ g.Border.FgColor = ui.ColorWhite
+ g.Border.LabelFgColor = ui.ColorCyan
+
+ spark := ui.Sparkline{}
+ spark.Height = 1
+ spark.Title = "srv 0:"
+ spdata := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6, 4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6, 4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6, 4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6}
+ spark.Data = spdata
+ spark.LineColor = ui.ColorCyan
+ spark.TitleColor = ui.ColorWhite
+
+ spark1 := ui.Sparkline{}
+ spark1.Height = 1
+ spark1.Title = "srv 1:"
+ spark1.Data = spdata
+ spark1.TitleColor = ui.ColorWhite
+ spark1.LineColor = ui.ColorRed
+
+ sp := ui.NewSparklines(spark, spark1)
+ sp.Width = 25
+ sp.Height = 7
+ sp.Border.Label = "Sparkline"
+ sp.Y = 4
+ sp.X = 25
+
+ sinps := (func() []float64 {
+ n := 220
+ ps := make([]float64, n)
+ for i := range ps {
+ ps[i] = 1 + math.Sin(float64(i)/5)
+ }
+ return ps
+ })()
+
+ lc := ui.NewLineChart()
+ lc.Border.Label = "dot-mode Line Chart"
+ lc.Data = sinps
+ lc.Width = 50
+ lc.Height = 11
+ lc.X = 0
+ lc.Y = 14
+ lc.AxesColor = ui.ColorWhite
+ lc.LineColor = ui.ColorRed | ui.AttrBold
+ lc.Mode = "dot"
+
+ bc := ui.NewBarChart()
+ bcdata := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6}
+ bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
+ bc.Border.Label = "Bar Chart"
+ bc.Width = 26
+ bc.Height = 10
+ bc.X = 51
+ bc.Y = 0
+ bc.DataLabels = bclabels
+ bc.BarColor = ui.ColorGreen
+ bc.NumColor = ui.ColorBlack
+
+ lc1 := ui.NewLineChart()
+ lc1.Border.Label = "braille-mode Line Chart"
+ lc1.Data = sinps
+ lc1.Width = 26
+ lc1.Height = 11
+ lc1.X = 51
+ lc1.Y = 14
+ lc1.AxesColor = ui.ColorWhite
+ lc1.LineColor = ui.ColorYellow | ui.AttrBold
+
+ p1 := ui.NewPar("Hey!\nI am a borderless block!")
+ p1.HasBorder = false
+ p1.Width = 26
+ p1.Height = 2
+ p1.TextFgColor = ui.ColorMagenta
+ p1.X = 52
+ p1.Y = 11
+
+ draw := func(t int) {
+ g.Percent = t % 101
+ list.Items = strs[t%9:]
+ sp.Lines[0].Data = spdata[:30+t%50]
+ sp.Lines[1].Data = spdata[:35+t%50]
+ lc.Data = sinps[t/2:]
+ lc1.Data = sinps[2*t:]
+ bc.Data = bcdata[t/2%10:]
+ ui.Render(p, list, g, sp, lc, bc, lc1, p1)
+ }
+
+ evt := ui.EventCh()
+
+ i := 0
+ for {
+ select {
+ case e := <-evt:
+ if e.Type == ui.EventKey && e.Ch == 'q' {
+ return
+ }
+ default:
+ draw(i)
+ i++
+ if i == 102 {
+ return
+ }
+ time.Sleep(time.Second / 2)
+ }
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.go b/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.go
new file mode 100644
index 000000000..b7033580f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.go
@@ -0,0 +1,62 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "github.com/gizak/termui"
+
+func main() {
+ err := termui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer termui.Close()
+
+ termui.UseTheme("helloworld")
+
+ g0 := termui.NewGauge()
+ g0.Percent = 40
+ g0.Width = 50
+ g0.Height = 3
+ g0.Border.Label = "Slim Gauge"
+ g0.BarColor = termui.ColorRed
+ g0.Border.FgColor = termui.ColorWhite
+ g0.Border.LabelFgColor = termui.ColorCyan
+
+ g2 := termui.NewGauge()
+ g2.Percent = 60
+ g2.Width = 50
+ g2.Height = 3
+ g2.PercentColor = termui.ColorBlue
+ g2.Y = 3
+ g2.Border.Label = "Slim Gauge"
+ g2.BarColor = termui.ColorYellow
+ g2.Border.FgColor = termui.ColorWhite
+
+ g1 := termui.NewGauge()
+ g1.Percent = 30
+ g1.Width = 50
+ g1.Height = 5
+ g1.Y = 6
+ g1.Border.Label = "Big Gauge"
+ g1.PercentColor = termui.ColorYellow
+ g1.BarColor = termui.ColorGreen
+ g1.Border.FgColor = termui.ColorWhite
+ g1.Border.LabelFgColor = termui.ColorMagenta
+
+ g3 := termui.NewGauge()
+ g3.Percent = 50
+ g3.Width = 50
+ g3.Height = 3
+ g3.Y = 11
+ g3.Border.Label = "Gauge with custom label"
+ g3.Label = "{{percent}}% (100MBs free)"
+ g3.LabelAlign = termui.AlignRight
+
+ termui.Render(g0, g1, g2, g3)
+
+ <-termui.EventCh()
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.png b/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.png
new file mode 100644
index 000000000..5c20e6e8a
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif
new file mode 100644
index 000000000..7490043de
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/grid.go b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.go
new file mode 100644
index 000000000..49121411f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.go
@@ -0,0 +1,134 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import ui "github.com/gizak/termui"
+import "math"
+import "time"
+
+func main() {
+ err := ui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer ui.Close()
+
+ sinps := (func() []float64 {
+ n := 400
+ ps := make([]float64, n)
+ for i := range ps {
+ ps[i] = 1 + math.Sin(float64(i)/5)
+ }
+ return ps
+ })()
+ sinpsint := (func() []int {
+ ps := make([]int, len(sinps))
+ for i, v := range sinps {
+ ps[i] = int(100*v + 10)
+ }
+ return ps
+ })()
+
+ ui.UseTheme("helloworld")
+
+ spark := ui.Sparkline{}
+ spark.Height = 8
+ spdata := sinpsint
+ spark.Data = spdata[:100]
+ spark.LineColor = ui.ColorCyan
+ spark.TitleColor = ui.ColorWhite
+
+ sp := ui.NewSparklines(spark)
+ sp.Height = 11
+ sp.Border.Label = "Sparkline"
+
+ lc := ui.NewLineChart()
+ lc.Border.Label = "braille-mode Line Chart"
+ lc.Data = sinps
+ lc.Height = 11
+ lc.AxesColor = ui.ColorWhite
+ lc.LineColor = ui.ColorYellow | ui.AttrBold
+
+ gs := make([]*ui.Gauge, 3)
+ for i := range gs {
+ gs[i] = ui.NewGauge()
+ gs[i].Height = 2
+ gs[i].HasBorder = false
+ gs[i].Percent = i * 10
+ gs[i].PaddingBottom = 1
+ gs[i].BarColor = ui.ColorRed
+ }
+
+ ls := ui.NewList()
+ ls.HasBorder = false
+ ls.Items = []string{
+ "[1] Downloading File 1",
+ "", // == \newline
+ "[2] Downloading File 2",
+ "",
+ "[3] Uploading File 3",
+ }
+ ls.Height = 5
+
+ par := ui.NewPar("<> This row has 3 columns\n<- Widgets can be stacked up like left side\n<- Stacked widgets are treated as a single widget")
+ par.Height = 5
+ par.Border.Label = "Demonstration"
+
+ // build layout
+ ui.Body.AddRows(
+ ui.NewRow(
+ ui.NewCol(6, 0, sp),
+ ui.NewCol(6, 0, lc)),
+ ui.NewRow(
+ ui.NewCol(3, 0, ls),
+ ui.NewCol(3, 0, gs[0], gs[1], gs[2]),
+ ui.NewCol(6, 0, par)))
+
+ // calculate layout
+ ui.Body.Align()
+
+ done := make(chan bool)
+ redraw := make(chan bool)
+
+ update := func() {
+ for i := 0; i < 103; i++ {
+ for _, g := range gs {
+ g.Percent = (g.Percent + 3) % 100
+ }
+
+ sp.Lines[0].Data = spdata[:100+i]
+ lc.Data = sinps[2*i:]
+
+ time.Sleep(time.Second / 2)
+ redraw <- true
+ }
+ done <- true
+ }
+
+ evt := ui.EventCh()
+
+ ui.Render(ui.Body)
+ go update()
+
+ for {
+ select {
+ case e := <-evt:
+ if e.Type == ui.EventKey && e.Ch == 'q' {
+ return
+ }
+ if e.Type == ui.EventResize {
+ ui.Body.Width = ui.TermWidth()
+ ui.Body.Align()
+ go func() { redraw <- true }()
+ }
+ case <-done:
+ return
+ case <-redraw:
+ ui.Render(ui.Body)
+ }
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.go b/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.go
new file mode 100644
index 000000000..1db543496
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.go
@@ -0,0 +1,68 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import (
+ "math"
+
+ "github.com/gizak/termui"
+)
+
+func main() {
+ err := termui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer termui.Close()
+
+ termui.UseTheme("helloworld")
+
+ sinps := (func() []float64 {
+ n := 220
+ ps := make([]float64, n)
+ for i := range ps {
+ ps[i] = 1 + math.Sin(float64(i)/5)
+ }
+ return ps
+ })()
+
+ lc0 := termui.NewLineChart()
+ lc0.Border.Label = "braille-mode Line Chart"
+ lc0.Data = sinps
+ lc0.Width = 50
+ lc0.Height = 12
+ lc0.X = 0
+ lc0.Y = 0
+ lc0.AxesColor = termui.ColorWhite
+ lc0.LineColor = termui.ColorGreen | termui.AttrBold
+
+ lc1 := termui.NewLineChart()
+ lc1.Border.Label = "dot-mode Line Chart"
+ lc1.Mode = "dot"
+ lc1.Data = sinps
+ lc1.Width = 26
+ lc1.Height = 12
+ lc1.X = 51
+ lc1.DotStyle = '+'
+ lc1.AxesColor = termui.ColorWhite
+ lc1.LineColor = termui.ColorYellow | termui.AttrBold
+
+ lc2 := termui.NewLineChart()
+ lc2.Border.Label = "dot-mode Line Chart"
+ lc2.Mode = "dot"
+ lc2.Data = sinps[4:]
+ lc2.Width = 77
+ lc2.Height = 16
+ lc2.X = 0
+ lc2.Y = 12
+ lc2.AxesColor = termui.ColorWhite
+ lc2.LineColor = termui.ColorCyan | termui.AttrBold
+
+ termui.Render(lc0, lc1, lc2)
+
+ <-termui.EventCh()
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.png b/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.png
new file mode 100644
index 000000000..655ef435f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/list.go b/Godeps/_workspace/src/github.com/gizak/termui/example/list.go
new file mode 100644
index 000000000..d33a3616c
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/list.go
@@ -0,0 +1,41 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "github.com/gizak/termui"
+
+func main() {
+ err := termui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer termui.Close()
+
+ termui.UseTheme("helloworld")
+
+ strs := []string{
+ "[0] github.com/gizak/termui",
+ "[1] 你好,世界",
+ "[2] こんにちは世界",
+ "[3] keyboard.go",
+ "[4] output.go",
+ "[5] random_out.go",
+ "[6] dashboard.go",
+ "[7] nsf/termbox-go"}
+
+ ls := termui.NewList()
+ ls.Items = strs
+ ls.ItemFgColor = termui.ColorYellow
+ ls.Border.Label = "List"
+ ls.Height = 7
+ ls.Width = 25
+ ls.Y = 0
+
+ termui.Render(ls)
+
+ <-termui.EventCh()
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/list.png b/Godeps/_workspace/src/github.com/gizak/termui/example/list.png
new file mode 100644
index 000000000..8ca08c079
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/list.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.go b/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.go
new file mode 100644
index 000000000..a32a28e0a
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.go
@@ -0,0 +1,50 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "github.com/gizak/termui"
+
+func main() {
+ err := termui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer termui.Close()
+
+ termui.UseTheme("helloworld")
+
+ bc := termui.NewMBarChart()
+ math := []int{90, 85, 90, 80}
+ english := []int{70, 85, 75, 60}
+ science := []int{75, 60, 80, 85}
+ compsci := []int{100, 100, 100, 100}
+ bc.Data[0] = math
+ bc.Data[1] = english
+ bc.Data[2] = science
+ bc.Data[3] = compsci
+ studentsName := []string{"Ken", "Rob", "Dennis", "Linus"}
+ bc.Border.Label = "Student's Marks X-Axis=Name Y-Axis=Marks[Math,English,Science,ComputerScience] in %"
+ bc.Width = 100
+ bc.Height = 50
+ bc.Y = 10
+ bc.BarWidth = 10
+ bc.DataLabels = studentsName
+ bc.ShowScale = true //Show y_axis scale value (min and max)
+ bc.SetMax(400)
+
+ bc.TextColor = termui.ColorGreen //this is color for label (x-axis)
+ bc.BarColor[3] = termui.ColorGreen //BarColor for computerscience
+ bc.BarColor[1] = termui.ColorYellow //Bar Color for english
+ bc.NumColor[3] = termui.ColorRed // Num color for computerscience
+ bc.NumColor[1] = termui.ColorRed // num color for english
+
+ //Other colors are automatically populated, btw All the students seems do well in computerscience. :p
+
+ termui.Render(bc)
+
+ <-termui.EventCh()
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.png b/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.png
new file mode 100644
index 000000000..9a4252616
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/par.go b/Godeps/_workspace/src/github.com/gizak/termui/example/par.go
new file mode 100644
index 000000000..ffbc60aa8
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/par.go
@@ -0,0 +1,48 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "github.com/gizak/termui"
+
+func main() {
+ err := termui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer termui.Close()
+
+ termui.UseTheme("helloworld")
+
+ par0 := termui.NewPar("Borderless Text")
+ par0.Height = 1
+ par0.Width = 20
+ par0.Y = 1
+ par0.HasBorder = false
+
+ par1 := termui.NewPar("你好,世界。")
+ par1.Height = 3
+ par1.Width = 17
+ par1.X = 20
+ par1.Border.Label = "标签"
+
+ par2 := termui.NewPar("Simple text\nwith label. It can be multilined with \\n or break automatically")
+ par2.Height = 5
+ par2.Width = 37
+ par2.Y = 4
+ par2.Border.Label = "Multiline"
+ par2.Border.FgColor = termui.ColorYellow
+
+ par3 := termui.NewPar("Long text with label and it is auto trimmed.")
+ par3.Height = 3
+ par3.Width = 37
+ par3.Y = 9
+ par3.Border.Label = "Auto Trim"
+
+ termui.Render(par0, par1, par2, par3)
+
+ <-termui.EventCh()
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/par.png b/Godeps/_workspace/src/github.com/gizak/termui/example/par.png
new file mode 100644
index 000000000..a85e64415
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/par.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.go b/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.go
new file mode 100644
index 000000000..f04baf570
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.go
@@ -0,0 +1,65 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "github.com/gizak/termui"
+
+func main() {
+ err := termui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer termui.Close()
+
+ termui.UseTheme("helloworld")
+
+ data := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6}
+ spl0 := termui.NewSparkline()
+ spl0.Data = data[3:]
+ spl0.Title = "Sparkline 0"
+ spl0.LineColor = termui.ColorGreen
+
+ // single
+ spls0 := termui.NewSparklines(spl0)
+ spls0.Height = 2
+ spls0.Width = 20
+ spls0.HasBorder = false
+
+ spl1 := termui.NewSparkline()
+ spl1.Data = data
+ spl1.Title = "Sparkline 1"
+ spl1.LineColor = termui.ColorRed
+
+ spl2 := termui.NewSparkline()
+ spl2.Data = data[5:]
+ spl2.Title = "Sparkline 2"
+ spl2.LineColor = termui.ColorMagenta
+
+ // group
+ spls1 := termui.NewSparklines(spl0, spl1, spl2)
+ spls1.Height = 8
+ spls1.Width = 20
+ spls1.Y = 3
+ spls1.Border.Label = "Group Sparklines"
+
+ spl3 := termui.NewSparkline()
+ spl3.Data = data
+ spl3.Title = "Enlarged Sparkline"
+ spl3.Height = 8
+ spl3.LineColor = termui.ColorYellow
+
+ spls2 := termui.NewSparklines(spl3)
+ spls2.Height = 11
+ spls2.Width = 30
+ spls2.Border.FgColor = termui.ColorCyan
+ spls2.X = 21
+ spls2.Border.Label = "Tweeked Sparkline"
+
+ termui.Render(spls0, spls1, spls2)
+
+ <-termui.EventCh()
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.png b/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.png
new file mode 100644
index 000000000..9dd7c82b1
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/theme.go b/Godeps/_workspace/src/github.com/gizak/termui/example/theme.go
new file mode 100644
index 000000000..30c51a343
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/theme.go
@@ -0,0 +1,143 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import ui "github.com/gizak/termui"
+import "math"
+
+import "time"
+
+func main() {
+ err := ui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer ui.Close()
+
+ ui.UseTheme("helloworld")
+
+ p := ui.NewPar(":PRESS q TO QUIT DEMO")
+ p.Height = 3
+ p.Width = 50
+ p.Border.Label = "Text Box"
+
+ strs := []string{"[0] gizak/termui", "[1] editbox.go", "[2] iterrupt.go", "[3] keyboard.go", "[4] output.go", "[5] random_out.go", "[6] dashboard.go", "[7] nsf/termbox-go"}
+ list := ui.NewList()
+ list.Items = strs
+ list.Border.Label = "List"
+ list.Height = 7
+ list.Width = 25
+ list.Y = 4
+
+ g := ui.NewGauge()
+ g.Percent = 50
+ g.Width = 50
+ g.Height = 3
+ g.Y = 11
+ g.Border.Label = "Gauge"
+
+ spark := ui.NewSparkline()
+ spark.Title = "srv 0:"
+ spdata := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6}
+ spark.Data = spdata
+
+ spark1 := ui.NewSparkline()
+ spark1.Title = "srv 1:"
+ spark1.Data = spdata
+
+ sp := ui.NewSparklines(spark, spark1)
+ sp.Width = 25
+ sp.Height = 7
+ sp.Border.Label = "Sparkline"
+ sp.Y = 4
+ sp.X = 25
+
+ lc := ui.NewLineChart()
+ sinps := (func() []float64 {
+ n := 100
+ ps := make([]float64, n)
+ for i := range ps {
+ ps[i] = 1 + math.Sin(float64(i)/4)
+ }
+ return ps
+ })()
+
+ lc.Border.Label = "Line Chart"
+ lc.Data = sinps
+ lc.Width = 50
+ lc.Height = 11
+ lc.X = 0
+ lc.Y = 14
+ lc.Mode = "dot"
+
+ bc := ui.NewBarChart()
+ bcdata := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6}
+ bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
+ bc.Border.Label = "Bar Chart"
+ bc.Width = 26
+ bc.Height = 10
+ bc.X = 51
+ bc.Y = 0
+ bc.DataLabels = bclabels
+
+ lc1 := ui.NewLineChart()
+ lc1.Border.Label = "Line Chart"
+ rndwalk := (func() []float64 {
+ n := 150
+ d := make([]float64, n)
+ for i := 1; i < n; i++ {
+ if i < 20 {
+ d[i] = d[i-1] + 0.01
+ }
+ if i > 20 {
+ d[i] = d[i-1] - 0.05
+ }
+ }
+ return d
+ })()
+ lc1.Data = rndwalk
+ lc1.Width = 26
+ lc1.Height = 11
+ lc1.X = 51
+ lc1.Y = 14
+
+ p1 := ui.NewPar("Hey!\nI am a borderless block!")
+ p1.HasBorder = false
+ p1.Width = 26
+ p1.Height = 2
+ p1.X = 52
+ p1.Y = 11
+
+ draw := func(t int) {
+ g.Percent = t % 101
+ list.Items = strs[t%9:]
+ sp.Lines[0].Data = spdata[t%10:]
+ sp.Lines[1].Data = spdata[t/2%10:]
+ lc.Data = sinps[t/2:]
+ lc1.Data = rndwalk[t:]
+ bc.Data = bcdata[t/2%10:]
+ ui.Render(p, list, g, sp, lc, bc, lc1, p1)
+ }
+
+ evt := ui.EventCh()
+ i := 0
+ for {
+ select {
+ case e := <-evt:
+ if e.Type == ui.EventKey && e.Ch == 'q' {
+ return
+ }
+ default:
+ draw(i)
+ i++
+ if i == 102 {
+ return
+ }
+ time.Sleep(time.Second / 2)
+ }
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.png b/Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.png
new file mode 100644
index 000000000..23b574f96
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png b/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png
new file mode 100644
index 000000000..eaf4d9303
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/gauge.go b/Godeps/_workspace/src/github.com/gizak/termui/gauge.go
new file mode 100644
index 000000000..986f4f3dc
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/gauge.go
@@ -0,0 +1,113 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "strconv"
+ "strings"
+)
+
+// Gauge is a progress bar like widget.
+// A simple example:
+/*
+ g := termui.NewGauge()
+ g.Percent = 40
+ g.Width = 50
+ g.Height = 3
+ g.Border.Label = "Slim Gauge"
+ g.BarColor = termui.ColorRed
+ g.PercentColor = termui.ColorBlue
+*/
+
+// Align is the position of the gauge's label.
+type Align int
+
+// All supported positions.
+const (
+ AlignLeft Align = iota
+ AlignCenter
+ AlignRight
+)
+
+type Gauge struct {
+ Block
+ Percent int
+ BarColor Attribute
+ PercentColor Attribute
+ Label string
+ LabelAlign Align
+}
+
+// NewGauge return a new gauge with current theme.
+func NewGauge() *Gauge {
+ g := &Gauge{
+ Block: *NewBlock(),
+ PercentColor: theme.GaugePercent,
+ BarColor: theme.GaugeBar,
+ Label: "{{percent}}%",
+ LabelAlign: AlignCenter,
+ }
+
+ g.Width = 12
+ g.Height = 5
+ return g
+}
+
+// Buffer implements Bufferer interface.
+func (g *Gauge) Buffer() []Point {
+ ps := g.Block.Buffer()
+
+ // plot bar
+ w := g.Percent * g.innerWidth / 100
+ for i := 0; i < g.innerHeight; i++ {
+ for j := 0; j < w; j++ {
+ p := Point{}
+ p.X = g.innerX + j
+ p.Y = g.innerY + i
+ p.Ch = ' '
+ p.Bg = g.BarColor
+ if p.Bg == ColorDefault {
+ p.Bg |= AttrReverse
+ }
+ ps = append(ps, p)
+ }
+ }
+
+ // plot percentage
+ s := strings.Replace(g.Label, "{{percent}}", strconv.Itoa(g.Percent), -1)
+ pry := g.innerY + g.innerHeight/2
+ rs := str2runes(s)
+ var pos int
+ switch g.LabelAlign {
+ case AlignLeft:
+ pos = 0
+
+ case AlignCenter:
+ pos = (g.innerWidth - strWidth(s)) / 2
+
+ case AlignRight:
+ pos = g.innerWidth - strWidth(s)
+ }
+
+ for i, v := range rs {
+ p := Point{}
+ p.X = 1 + pos + i
+ p.Y = pry
+ p.Ch = v
+ p.Fg = g.PercentColor
+ if w+g.innerX > pos+i {
+ p.Bg = g.BarColor
+ if p.Bg == ColorDefault {
+ p.Bg |= AttrReverse
+ }
+
+ } else {
+ p.Bg = g.Block.BgColor
+ }
+
+ ps = append(ps, p)
+ }
+ return g.Block.chopOverflow(ps)
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/grid.go b/Godeps/_workspace/src/github.com/gizak/termui/grid.go
new file mode 100644
index 000000000..5f6e85e76
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/grid.go
@@ -0,0 +1,279 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+// GridBufferer introduces a Bufferer that can be manipulated by Grid.
+type GridBufferer interface {
+ Bufferer
+ GetHeight() int
+ SetWidth(int)
+ SetX(int)
+ SetY(int)
+}
+
+// Row builds a layout tree
+type Row struct {
+ Cols []*Row //children
+ Widget GridBufferer // root
+ X int
+ Y int
+ Width int
+ Height int
+ Span int
+ Offset int
+}
+
+// calculate and set the underlying layout tree's x, y, height and width.
+func (r *Row) calcLayout() {
+ r.assignWidth(r.Width)
+ r.Height = r.solveHeight()
+ r.assignX(r.X)
+ r.assignY(r.Y)
+}
+
+// tell if the node is leaf in the tree.
+func (r *Row) isLeaf() bool {
+ return r.Cols == nil || len(r.Cols) == 0
+}
+
+func (r *Row) isRenderableLeaf() bool {
+ return r.isLeaf() && r.Widget != nil
+}
+
+// assign widgets' (and their parent rows') width recursively.
+func (r *Row) assignWidth(w int) {
+ r.SetWidth(w)
+
+ accW := 0 // acc span and offset
+ calcW := make([]int, len(r.Cols)) // calculated width
+ calcOftX := make([]int, len(r.Cols)) // computated start position of x
+
+ for i, c := range r.Cols {
+ accW += c.Span + c.Offset
+ cw := int(float64(c.Span*r.Width) / 12.0)
+
+ if i >= 1 {
+ calcOftX[i] = calcOftX[i-1] +
+ calcW[i-1] +
+ int(float64(r.Cols[i-1].Offset*r.Width)/12.0)
+ }
+
+ // use up the space if it is the last col
+ if i == len(r.Cols)-1 && accW == 12 {
+ cw = r.Width - calcOftX[i]
+ }
+ calcW[i] = cw
+ r.Cols[i].assignWidth(cw)
+ }
+}
+
+// bottom up calc and set rows' (and their widgets') height,
+// return r's total height.
+func (r *Row) solveHeight() int {
+ if r.isRenderableLeaf() {
+ r.Height = r.Widget.GetHeight()
+ return r.Widget.GetHeight()
+ }
+
+ maxh := 0
+ if !r.isLeaf() {
+ for _, c := range r.Cols {
+ nh := c.solveHeight()
+ // when embed rows in Cols, row widgets stack up
+ if r.Widget != nil {
+ nh += r.Widget.GetHeight()
+ }
+ if nh > maxh {
+ maxh = nh
+ }
+ }
+ }
+
+ r.Height = maxh
+ return maxh
+}
+
+// recursively assign x position for r tree.
+func (r *Row) assignX(x int) {
+ r.SetX(x)
+
+ if !r.isLeaf() {
+ acc := 0
+ for i, c := range r.Cols {
+ if c.Offset != 0 {
+ acc += int(float64(c.Offset*r.Width) / 12.0)
+ }
+ r.Cols[i].assignX(x + acc)
+ acc += c.Width
+ }
+ }
+}
+
+// recursively assign y position to r.
+func (r *Row) assignY(y int) {
+ r.SetY(y)
+
+ if r.isLeaf() {
+ return
+ }
+
+ for i := range r.Cols {
+ acc := 0
+ if r.Widget != nil {
+ acc = r.Widget.GetHeight()
+ }
+ r.Cols[i].assignY(y + acc)
+ }
+
+}
+
+// GetHeight implements GridBufferer interface.
+func (r Row) GetHeight() int {
+ return r.Height
+}
+
+// SetX implements GridBufferer interface.
+func (r *Row) SetX(x int) {
+ r.X = x
+ if r.Widget != nil {
+ r.Widget.SetX(x)
+ }
+}
+
+// SetY implements GridBufferer interface.
+func (r *Row) SetY(y int) {
+ r.Y = y
+ if r.Widget != nil {
+ r.Widget.SetY(y)
+ }
+}
+
+// SetWidth implements GridBufferer interface.
+func (r *Row) SetWidth(w int) {
+ r.Width = w
+ if r.Widget != nil {
+ r.Widget.SetWidth(w)
+ }
+}
+
+// Buffer implements Bufferer interface,
+// recursively merge all widgets buffer
+func (r *Row) Buffer() []Point {
+ merged := []Point{}
+
+ if r.isRenderableLeaf() {
+ return r.Widget.Buffer()
+ }
+
+ // for those are not leaves but have a renderable widget
+ if r.Widget != nil {
+ merged = append(merged, r.Widget.Buffer()...)
+ }
+
+ // collect buffer from children
+ if !r.isLeaf() {
+ for _, c := range r.Cols {
+ merged = append(merged, c.Buffer()...)
+ }
+ }
+
+ return merged
+}
+
+// Grid implements 12 columns system.
+// A simple example:
+/*
+ import ui "github.com/gizak/termui"
+ // init and create widgets...
+
+ // build
+ ui.Body.AddRows(
+ ui.NewRow(
+ ui.NewCol(6, 0, widget0),
+ ui.NewCol(6, 0, widget1)),
+ ui.NewRow(
+ ui.NewCol(3, 0, widget2),
+ ui.NewCol(3, 0, widget30, widget31, widget32),
+ ui.NewCol(6, 0, widget4)))
+
+ // calculate layout
+ ui.Body.Align()
+
+ ui.Render(ui.Body)
+*/
+type Grid struct {
+ Rows []*Row
+ Width int
+ X int
+ Y int
+ BgColor Attribute
+}
+
+// NewGrid returns *Grid with given rows.
+func NewGrid(rows ...*Row) *Grid {
+ return &Grid{Rows: rows}
+}
+
+// AddRows appends given rows to Grid.
+func (g *Grid) AddRows(rs ...*Row) {
+ g.Rows = append(g.Rows, rs...)
+}
+
+// NewRow creates a new row out of given columns.
+func NewRow(cols ...*Row) *Row {
+ rs := &Row{Span: 12, Cols: cols}
+ return rs
+}
+
+// NewCol accepts: widgets are LayoutBufferer or widgets is A NewRow.
+// Note that if multiple widgets are provided, they will stack up in the col.
+func NewCol(span, offset int, widgets ...GridBufferer) *Row {
+ r := &Row{Span: span, Offset: offset}
+
+ if widgets != nil && len(widgets) == 1 {
+ wgt := widgets[0]
+ nw, isRow := wgt.(*Row)
+ if isRow {
+ r.Cols = nw.Cols
+ } else {
+ r.Widget = wgt
+ }
+ return r
+ }
+
+ r.Cols = []*Row{}
+ ir := r
+ for _, w := range widgets {
+ nr := &Row{Span: 12, Widget: w}
+ ir.Cols = []*Row{nr}
+ ir = nr
+ }
+
+ return r
+}
+
+// Align calculate each rows' layout.
+func (g *Grid) Align() {
+ h := 0
+ for _, r := range g.Rows {
+ r.SetWidth(g.Width)
+ r.SetX(g.X)
+ r.SetY(g.Y + h)
+ r.calcLayout()
+ h += r.GetHeight()
+ }
+}
+
+// Buffer implments Bufferer interface.
+func (g Grid) Buffer() []Point {
+ ps := []Point{}
+ for _, r := range g.Rows {
+ ps = append(ps, r.Buffer()...)
+ }
+ return ps
+}
+
+// Body corresponds to the entire terminal display region.
+var Body *Grid
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/grid_test.go b/Godeps/_workspace/src/github.com/gizak/termui/grid_test.go
new file mode 100644
index 000000000..cdafb2052
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/grid_test.go
@@ -0,0 +1,98 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "testing"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+var r *Row
+
+func TestRowWidth(t *testing.T) {
+ p0 := NewPar("p0")
+ p0.Height = 1
+ p1 := NewPar("p1")
+ p1.Height = 1
+ p2 := NewPar("p2")
+ p2.Height = 1
+ p3 := NewPar("p3")
+ p3.Height = 1
+
+ /* test against tree:
+
+ r
+ / \
+ 0:w 1
+ / \
+ 10:w 11
+ /
+ 110:w
+ /
+ 1100:w
+ */
+ /*
+ r = &row{
+ Span: 12,
+ Cols: []*row{
+ &row{Widget: p0, Span: 6},
+ &row{
+ Span: 6,
+ Cols: []*row{
+ &row{Widget: p1, Span: 6},
+ &row{
+ Span: 6,
+ Cols: []*row{
+ &row{
+ Span: 12,
+ Widget: p2,
+ Cols: []*row{
+ &row{Span: 12, Widget: p3}}}}}}}}}
+ */
+
+ r = NewRow(
+ NewCol(6, 0, p0),
+ NewCol(6, 0,
+ NewRow(
+ NewCol(6, 0, p1),
+ NewCol(6, 0, p2, p3))))
+
+ r.assignWidth(100)
+ if r.Width != 100 ||
+ (r.Cols[0].Width) != 50 ||
+ (r.Cols[1].Width) != 50 ||
+ (r.Cols[1].Cols[0].Width) != 25 ||
+ (r.Cols[1].Cols[1].Width) != 25 ||
+ (r.Cols[1].Cols[1].Cols[0].Width) != 25 ||
+ (r.Cols[1].Cols[1].Cols[0].Cols[0].Width) != 25 {
+ t.Error("assignWidth fails")
+ }
+}
+
+func TestRowHeight(t *testing.T) {
+ spew.Dump()
+
+ if (r.solveHeight()) != 2 ||
+ (r.Cols[1].Cols[1].Height) != 2 ||
+ (r.Cols[1].Cols[1].Cols[0].Height) != 2 ||
+ (r.Cols[1].Cols[0].Height) != 1 {
+ t.Error("solveHeight fails")
+ }
+}
+
+func TestAssignXY(t *testing.T) {
+ r.assignX(0)
+ r.assignY(0)
+ if (r.Cols[0].X) != 0 ||
+ (r.Cols[1].Cols[0].X) != 50 ||
+ (r.Cols[1].Cols[1].X) != 75 ||
+ (r.Cols[1].Cols[1].Cols[0].X) != 75 ||
+ (r.Cols[1].Cols[0].Y) != 0 ||
+ (r.Cols[1].Cols[1].Cols[0].Y) != 0 ||
+ (r.Cols[1].Cols[1].Cols[0].Cols[0].Y) != 1 {
+ t.Error("assignXY fails")
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/helper.go b/Godeps/_workspace/src/github.com/gizak/termui/helper.go
new file mode 100644
index 000000000..80d8a0278
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/helper.go
@@ -0,0 +1,66 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import tm "github.com/nsf/termbox-go"
+import rw "github.com/mattn/go-runewidth"
+
+/* ---------------Port from termbox-go --------------------- */
+
+// Attribute is printable cell's color and style.
+type Attribute uint16
+
+const (
+ ColorDefault Attribute = iota
+ ColorBlack
+ ColorRed
+ ColorGreen
+ ColorYellow
+ ColorBlue
+ ColorMagenta
+ ColorCyan
+ ColorWhite
+)
+
+const NumberofColors = 8 //Have a constant that defines number of colors
+const (
+ AttrBold Attribute = 1 << (iota + 9)
+ AttrUnderline
+ AttrReverse
+)
+
+var (
+ dot = "…"
+ dotw = rw.StringWidth(dot)
+)
+
+/* ----------------------- End ----------------------------- */
+
+func toTmAttr(x Attribute) tm.Attribute {
+ return tm.Attribute(x)
+}
+
+func str2runes(s string) []rune {
+ return []rune(s)
+}
+
+func trimStr2Runes(s string, w int) []rune {
+ if w <= 0 {
+ return []rune{}
+ }
+ sw := rw.StringWidth(s)
+ if sw > w {
+ return []rune(rw.Truncate(s, w, dot))
+ }
+ return str2runes(s) //[]rune(rw.Truncate(s, w, ""))
+}
+
+func strWidth(s string) int {
+ return rw.StringWidth(s)
+}
+
+func charWidth(ch rune) int {
+ return rw.RuneWidth(ch)
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/helper_test.go b/Godeps/_workspace/src/github.com/gizak/termui/helper_test.go
new file mode 100644
index 000000000..6d1a56130
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/helper_test.go
@@ -0,0 +1,58 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "testing"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+func TestStr2Rune(t *testing.T) {
+ s := "你好,世界."
+ rs := str2runes(s)
+ if len(rs) != 6 {
+ t.Error()
+ }
+}
+
+func TestWidth(t *testing.T) {
+ s0 := "つのだ☆HIRO"
+ s1 := "11111111111"
+ spew.Dump(s0)
+ spew.Dump(s1)
+ // above not align for setting East Asian Ambiguous to wide!!
+
+ if strWidth(s0) != strWidth(s1) {
+ t.Error("str len failed")
+ }
+
+ len1 := []rune{'a', '2', '&', '「', 'オ', '。'} //will false: 'ᆵ', 'ᄚ', 'ᄒ'
+ for _, v := range len1 {
+ if charWidth(v) != 1 {
+ t.Error("len1 failed")
+ }
+ }
+
+ len2 := []rune{'漢', '字', '한', '자', '你', '好', 'だ', '。', '%', 's', 'E', 'ョ', '、', 'ヲ'}
+ for _, v := range len2 {
+ if charWidth(v) != 2 {
+ t.Error("len2 failed")
+ }
+ }
+}
+
+func TestTrim(t *testing.T) {
+ s := "つのだ☆HIRO"
+ if string(trimStr2Runes(s, 10)) != "つのだ☆HI"+dot {
+ t.Error("trim failed")
+ }
+ if string(trimStr2Runes(s, 11)) != "つのだ☆HIRO" {
+ t.Error("avoid tail trim failed")
+ }
+ if string(trimStr2Runes(s, 15)) != "つのだ☆HIRO" {
+ t.Error("avoid trim failed")
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/list.go b/Godeps/_workspace/src/github.com/gizak/termui/list.go
new file mode 100644
index 000000000..0640932f2
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/list.go
@@ -0,0 +1,104 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import "strings"
+
+// List displays []string as its items,
+// it has a Overflow option (default is "hidden"), when set to "hidden",
+// the item exceeding List's width is truncated, but when set to "wrap",
+// the overflowed text breaks into next line.
+/*
+ strs := []string{
+ "[0] github.com/gizak/termui",
+ "[1] editbox.go",
+ "[2] iterrupt.go",
+ "[3] keyboard.go",
+ "[4] output.go",
+ "[5] random_out.go",
+ "[6] dashboard.go",
+ "[7] nsf/termbox-go"}
+
+ ls := termui.NewList()
+ ls.Items = strs
+ ls.ItemFgColor = termui.ColorYellow
+ ls.Border.Label = "List"
+ ls.Height = 7
+ ls.Width = 25
+ ls.Y = 0
+*/
+type List struct {
+ Block
+ Items []string
+ Overflow string
+ ItemFgColor Attribute
+ ItemBgColor Attribute
+}
+
+// NewList returns a new *List with current theme.
+func NewList() *List {
+ l := &List{Block: *NewBlock()}
+ l.Overflow = "hidden"
+ l.ItemFgColor = theme.ListItemFg
+ l.ItemBgColor = theme.ListItemBg
+ return l
+}
+
+// Buffer implements Bufferer interface.
+func (l *List) Buffer() []Point {
+ ps := l.Block.Buffer()
+ switch l.Overflow {
+ case "wrap":
+ rs := str2runes(strings.Join(l.Items, "\n"))
+ i, j, k := 0, 0, 0
+ for i < l.innerHeight && k < len(rs) {
+ w := charWidth(rs[k])
+ if rs[k] == '\n' || j+w > l.innerWidth {
+ i++
+ j = 0
+ if rs[k] == '\n' {
+ k++
+ }
+ continue
+ }
+ pi := Point{}
+ pi.X = l.innerX + j
+ pi.Y = l.innerY + i
+
+ pi.Ch = rs[k]
+ pi.Bg = l.ItemBgColor
+ pi.Fg = l.ItemFgColor
+
+ ps = append(ps, pi)
+ k++
+ j++
+ }
+
+ case "hidden":
+ trimItems := l.Items
+ if len(trimItems) > l.innerHeight {
+ trimItems = trimItems[:l.innerHeight]
+ }
+ for i, v := range trimItems {
+ rs := trimStr2Runes(v, l.innerWidth)
+
+ j := 0
+ for _, vv := range rs {
+ w := charWidth(vv)
+ p := Point{}
+ p.X = l.innerX + j
+ p.Y = l.innerY + i
+
+ p.Ch = vv
+ p.Bg = l.ItemBgColor
+ p.Fg = l.ItemFgColor
+
+ ps = append(ps, p)
+ j += w
+ }
+ }
+ }
+ return l.Block.chopOverflow(ps)
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/mbar.go b/Godeps/_workspace/src/github.com/gizak/termui/mbar.go
new file mode 100644
index 000000000..9d18c2cb4
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/mbar.go
@@ -0,0 +1,233 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "fmt"
+)
+
+// This is the implemetation of multi-colored or stacked bar graph. This is different from default barGraph which is implemented in bar.go
+// Multi-Colored-BarChart creates multiple bars in a widget:
+/*
+ bc := termui.NewMBarChart()
+ data := make([][]int, 2)
+ data[0] := []int{3, 2, 5, 7, 9, 4}
+ data[1] := []int{7, 8, 5, 3, 1, 6}
+ bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
+ bc.Border.Label = "Bar Chart"
+ bc.Data = data
+ bc.Width = 26
+ bc.Height = 10
+ bc.DataLabels = bclabels
+ bc.TextColor = termui.ColorGreen
+ bc.BarColor = termui.ColorRed
+ bc.NumColor = termui.ColorYellow
+*/
+type MBarChart struct {
+ Block
+ BarColor [NumberofColors]Attribute
+ TextColor Attribute
+ NumColor [NumberofColors]Attribute
+ Data [NumberofColors][]int
+ DataLabels []string
+ BarWidth int
+ BarGap int
+ labels [][]rune
+ dataNum [NumberofColors][][]rune
+ numBar int
+ scale float64
+ max int
+ minDataLen int
+ numStack int
+ ShowScale bool
+ maxScale []rune
+}
+
+// NewBarChart returns a new *BarChart with current theme.
+func NewMBarChart() *MBarChart {
+ bc := &MBarChart{Block: *NewBlock()}
+ bc.BarColor[0] = theme.MBarChartBar
+ bc.NumColor[0] = theme.MBarChartNum
+ bc.TextColor = theme.MBarChartText
+ bc.BarGap = 1
+ bc.BarWidth = 3
+ return bc
+}
+
+func (bc *MBarChart) layout() {
+ bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth)
+ bc.labels = make([][]rune, bc.numBar)
+ DataLen := 0
+ LabelLen := len(bc.DataLabels)
+ bc.minDataLen = 9999 //Set this to some very hight value so that we find the minimum one We want to know which array among data[][] has got the least length
+
+ // We need to know how many stack/data array data[0] , data[1] are there
+ for i := 0; i < len(bc.Data); i++ {
+ if bc.Data[i] == nil {
+ break
+ }
+ DataLen++
+ }
+ bc.numStack = DataLen
+
+ //We need to know what is the mimimum size of data array data[0] could have 10 elements data[1] could have only 5, so we plot only 5 bar graphs
+
+ for i := 0; i < DataLen; i++ {
+ if bc.minDataLen > len(bc.Data[i]) {
+ bc.minDataLen = len(bc.Data[i])
+ }
+ }
+
+ if LabelLen > bc.minDataLen {
+ LabelLen = bc.minDataLen
+ }
+
+ for i := 0; i < LabelLen && i < bc.numBar; i++ {
+ bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
+ }
+
+ for i := 0; i < bc.numStack; i++ {
+ bc.dataNum[i] = make([][]rune, len(bc.Data[i]))
+ //For each stack of bar calcualte the rune
+ for j := 0; j < LabelLen && i < bc.numBar; j++ {
+ n := bc.Data[i][j]
+ s := fmt.Sprint(n)
+ bc.dataNum[i][j] = trimStr2Runes(s, bc.BarWidth)
+ }
+ //If color is not defined by default then populate a color that is different from the prevous bar
+ if bc.BarColor[i] == ColorDefault && bc.NumColor[i] == ColorDefault {
+ if i == 0 {
+ bc.BarColor[i] = ColorBlack
+ } else {
+ bc.BarColor[i] = bc.BarColor[i-1] + 1
+ if bc.BarColor[i] > NumberofColors {
+ bc.BarColor[i] = ColorBlack
+ }
+ }
+ bc.NumColor[i] = (NumberofColors + 1) - bc.BarColor[i] //Make NumColor opposite of barColor for visibility
+ }
+ }
+
+ //If Max value is not set then we have to populate, this time the max value will be max(sum(d1[0],d2[0],d3[0]) .... sum(d1[n], d2[n], d3[n]))
+
+ if bc.max == 0 {
+ bc.max = -1
+ }
+ for i := 0; i < bc.minDataLen && i < LabelLen; i++ {
+ var dsum int
+ for j := 0; j < bc.numStack; j++ {
+ dsum += bc.Data[j][i]
+ }
+ if dsum > bc.max {
+ bc.max = dsum
+ }
+ }
+
+ //Finally Calculate max sale
+ if bc.ShowScale {
+ s := fmt.Sprintf("%d", bc.max)
+ bc.maxScale = trimStr2Runes(s, len(s))
+ bc.scale = float64(bc.max) / float64(bc.innerHeight-2)
+ } else {
+ bc.scale = float64(bc.max) / float64(bc.innerHeight-1)
+ }
+
+}
+
+func (bc *MBarChart) SetMax(max int) {
+
+ if max > 0 {
+ bc.max = max
+ }
+}
+
+// Buffer implements Bufferer interface.
+func (bc *MBarChart) Buffer() []Point {
+ ps := bc.Block.Buffer()
+ bc.layout()
+ var oftX int
+
+ for i := 0; i < bc.numBar && i < bc.minDataLen && i < len(bc.DataLabels); i++ {
+ ph := 0 //Previous Height to stack up
+ oftX = i * (bc.BarWidth + bc.BarGap)
+ for i1 := 0; i1 < bc.numStack; i1++ {
+ h := int(float64(bc.Data[i1][i]) / bc.scale)
+ // plot bars
+ for j := 0; j < bc.BarWidth; j++ {
+ for k := 0; k < h; k++ {
+ p := Point{}
+ p.Ch = ' '
+ p.Bg = bc.BarColor[i1]
+ if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent!
+ p.Bg |= AttrReverse
+ }
+ p.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j
+ p.Y = bc.innerY + bc.innerHeight - 2 - k - ph
+ ps = append(ps, p)
+ }
+ }
+ ph += h
+ }
+ // plot text
+ for j, k := 0, 0; j < len(bc.labels[i]); j++ {
+ w := charWidth(bc.labels[i][j])
+ p := Point{}
+ p.Ch = bc.labels[i][j]
+ p.Bg = bc.BgColor
+ p.Fg = bc.TextColor
+ p.Y = bc.innerY + bc.innerHeight - 1
+ p.X = bc.innerX + oftX + ((bc.BarWidth - len(bc.labels[i])) / 2) + k
+ ps = append(ps, p)
+ k += w
+ }
+ // plot num
+ ph = 0 //re-initialize previous height
+ for i1 := 0; i1 < bc.numStack; i1++ {
+ h := int(float64(bc.Data[i1][i]) / bc.scale)
+ for j := 0; j < len(bc.dataNum[i1][i]) && h > 0; j++ {
+ p := Point{}
+ p.Ch = bc.dataNum[i1][i][j]
+ p.Fg = bc.NumColor[i1]
+ p.Bg = bc.BarColor[i1]
+ if bc.BarColor[i1] == ColorDefault { // the same as above
+ p.Bg |= AttrReverse
+ }
+ if h == 0 {
+ p.Bg = bc.BgColor
+ }
+ p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j
+ p.Y = bc.innerY + bc.innerHeight - 2 - ph
+ ps = append(ps, p)
+ }
+ ph += h
+ }
+ }
+
+ if bc.ShowScale {
+ //Currently bar graph only supprts data range from 0 to MAX
+ //Plot 0
+ p := Point{}
+ p.Ch = '0'
+ p.Bg = bc.BgColor
+ p.Fg = bc.TextColor
+ p.Y = bc.innerY + bc.innerHeight - 2
+ p.X = bc.X
+ ps = append(ps, p)
+
+ //Plot the maximum sacle value
+ for i := 0; i < len(bc.maxScale); i++ {
+ p := Point{}
+ p.Ch = bc.maxScale[i]
+ p.Bg = bc.BgColor
+ p.Fg = bc.TextColor
+ p.Y = bc.innerY
+ p.X = bc.X + i
+ ps = append(ps, p)
+ }
+
+ }
+
+ return bc.Block.chopOverflow(ps)
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/p.go b/Godeps/_workspace/src/github.com/gizak/termui/p.go
new file mode 100644
index 000000000..e327d7489
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/p.go
@@ -0,0 +1,71 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+// Par displays a paragraph.
+/*
+ par := termui.NewPar("Simple Text")
+ par.Height = 3
+ par.Width = 17
+ par.Border.Label = "Label"
+*/
+type Par struct {
+ Block
+ Text string
+ TextFgColor Attribute
+ TextBgColor Attribute
+}
+
+// NewPar returns a new *Par with given text as its content.
+func NewPar(s string) *Par {
+ return &Par{
+ Block: *NewBlock(),
+ Text: s,
+ TextFgColor: theme.ParTextFg,
+ TextBgColor: theme.ParTextBg}
+}
+
+// Buffer implements Bufferer interface.
+func (p *Par) Buffer() []Point {
+ ps := p.Block.Buffer()
+
+ rs := str2runes(p.Text)
+ i, j, k := 0, 0, 0
+ for i < p.innerHeight && k < len(rs) {
+ // the width of char is about to print
+ w := charWidth(rs[k])
+
+ if rs[k] == '\n' || j+w > p.innerWidth {
+ i++
+ j = 0 // set x = 0
+ if rs[k] == '\n' {
+ k++
+ }
+
+ if i >= p.innerHeight {
+ ps = append(ps, newPointWithAttrs('…',
+ p.innerX+p.innerWidth-1,
+ p.innerY+p.innerHeight-1,
+ p.TextFgColor, p.TextBgColor))
+ break
+ }
+
+ continue
+ }
+ pi := Point{}
+ pi.X = p.innerX + j
+ pi.Y = p.innerY + i
+
+ pi.Ch = rs[k]
+ pi.Bg = p.TextBgColor
+ pi.Fg = p.TextFgColor
+
+ ps = append(ps, pi)
+
+ k++
+ j += w
+ }
+ return p.Block.chopOverflow(ps)
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/point.go b/Godeps/_workspace/src/github.com/gizak/termui/point.go
new file mode 100644
index 000000000..c381af9a4
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/point.go
@@ -0,0 +1,28 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+// Point stands for a single cell in terminal.
+type Point struct {
+ Ch rune
+ Bg Attribute
+ Fg Attribute
+ X int
+ Y int
+}
+
+func newPoint(c rune, x, y int) (p Point) {
+ p.Ch = c
+ p.X = x
+ p.Y = y
+ return
+}
+
+func newPointWithAttrs(c rune, x, y int, fg, bg Attribute) Point {
+ p := newPoint(c, x, y)
+ p.Bg = bg
+ p.Fg = fg
+ return p
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/render.go b/Godeps/_workspace/src/github.com/gizak/termui/render.go
new file mode 100644
index 000000000..d697d0aea
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/render.go
@@ -0,0 +1,60 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import tm "github.com/nsf/termbox-go"
+
+// Bufferer should be implemented by all renderable components.
+type Bufferer interface {
+ Buffer() []Point
+}
+
+// Init initializes termui library. This function should be called before any others.
+// After initialization, the library must be finalized by 'Close' function.
+func Init() error {
+ Body = NewGrid()
+ Body.X = 0
+ Body.Y = 0
+ Body.BgColor = theme.BodyBg
+ defer func() {
+ w, _ := tm.Size()
+ Body.Width = w
+ evtListen()
+ }()
+ return tm.Init()
+}
+
+// Close finalizes termui library,
+// should be called after successful initialization when termui's functionality isn't required anymore.
+func Close() {
+ tm.Close()
+}
+
+// TermWidth returns the current terminal's width.
+func TermWidth() int {
+ tm.Sync()
+ w, _ := tm.Size()
+ return w
+}
+
+// TermHeight returns the current terminal's height.
+func TermHeight() int {
+ tm.Sync()
+ _, h := tm.Size()
+ return h
+}
+
+// Render renders all Bufferer in the given order from left to right,
+// right could overlap on left ones.
+func Render(rs ...Bufferer) {
+ tm.Clear(tm.ColorDefault, toTmAttr(theme.BodyBg))
+ for _, r := range rs {
+ buf := r.Buffer()
+ for _, v := range buf {
+ tm.SetCell(v.X, v.Y, v.Ch, toTmAttr(v.Fg), toTmAttr(v.Bg))
+ }
+ }
+ tm.Flush()
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/sparkline.go b/Godeps/_workspace/src/github.com/gizak/termui/sparkline.go
new file mode 100644
index 000000000..c63a5857f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/sparkline.go
@@ -0,0 +1,156 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import "math"
+
+// Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃
+/*
+ data := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1}
+ spl := termui.NewSparkline()
+ spl.Data = data
+ spl.Title = "Sparkline 0"
+ spl.LineColor = termui.ColorGreen
+*/
+type Sparkline struct {
+ Data []int
+ Height int
+ Title string
+ TitleColor Attribute
+ LineColor Attribute
+ displayHeight int
+ scale float32
+ max int
+}
+
+// Sparklines is a renderable widget which groups together the given sparklines.
+/*
+ spls := termui.NewSparklines(spl0,spl1,spl2) //...
+ spls.Height = 2
+ spls.Width = 20
+*/
+type Sparklines struct {
+ Block
+ Lines []Sparkline
+ displayLines int
+ displayWidth int
+}
+
+var sparks = []rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'}
+
+// Add appends a given Sparkline to s *Sparklines.
+func (s *Sparklines) Add(sl Sparkline) {
+ s.Lines = append(s.Lines, sl)
+}
+
+// NewSparkline returns a unrenderable single sparkline that intended to be added into Sparklines.
+func NewSparkline() Sparkline {
+ return Sparkline{
+ Height: 1,
+ TitleColor: theme.SparklineTitle,
+ LineColor: theme.SparklineLine}
+}
+
+// NewSparklines return a new *Spaklines with given Sparkline(s), you can always add a new Sparkline later.
+func NewSparklines(ss ...Sparkline) *Sparklines {
+ s := &Sparklines{Block: *NewBlock(), Lines: ss}
+ return s
+}
+
+func (sl *Sparklines) update() {
+ for i, v := range sl.Lines {
+ if v.Title == "" {
+ sl.Lines[i].displayHeight = v.Height
+ } else {
+ sl.Lines[i].displayHeight = v.Height + 1
+ }
+ }
+ sl.displayWidth = sl.innerWidth
+
+ // get how many lines gotta display
+ h := 0
+ sl.displayLines = 0
+ for _, v := range sl.Lines {
+ if h+v.displayHeight <= sl.innerHeight {
+ sl.displayLines++
+ } else {
+ break
+ }
+ h += v.displayHeight
+ }
+
+ for i := 0; i < sl.displayLines; i++ {
+ data := sl.Lines[i].Data
+
+ max := math.MinInt32
+ for _, v := range data {
+ if max < v {
+ max = v
+ }
+ }
+ sl.Lines[i].max = max
+ sl.Lines[i].scale = float32(8*sl.Lines[i].Height) / float32(max)
+ }
+}
+
+// Buffer implements Bufferer interface.
+func (sl *Sparklines) Buffer() []Point {
+ ps := sl.Block.Buffer()
+ sl.update()
+
+ oftY := 0
+ for i := 0; i < sl.displayLines; i++ {
+ l := sl.Lines[i]
+ data := l.Data
+
+ if len(data) > sl.innerWidth {
+ data = data[len(data)-sl.innerWidth:]
+ }
+
+ if l.Title != "" {
+ rs := trimStr2Runes(l.Title, sl.innerWidth)
+ oftX := 0
+ for _, v := range rs {
+ w := charWidth(v)
+ p := Point{}
+ p.Ch = v
+ p.Fg = l.TitleColor
+ p.Bg = sl.BgColor
+ p.X = sl.innerX + oftX
+ p.Y = sl.innerY + oftY
+ ps = append(ps, p)
+ oftX += w
+ }
+ }
+
+ for j, v := range data {
+ h := int(float32(v)*l.scale + 0.5)
+ barCnt := h / 8
+ barMod := h % 8
+ for jj := 0; jj < barCnt; jj++ {
+ p := Point{}
+ p.X = sl.innerX + j
+ p.Y = sl.innerY + oftY + l.Height - jj
+ p.Ch = ' ' // => sparks[7]
+ p.Bg = l.LineColor
+ //p.Bg = sl.BgColor
+ ps = append(ps, p)
+ }
+ if barMod != 0 {
+ p := Point{}
+ p.X = sl.innerX + j
+ p.Y = sl.innerY + oftY + l.Height - barCnt
+ p.Ch = sparks[barMod-1]
+ p.Fg = l.LineColor
+ p.Bg = sl.BgColor
+ ps = append(ps, p)
+ }
+ }
+
+ oftY += l.displayHeight
+ }
+
+ return sl.Block.chopOverflow(ps)
+}
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/theme.go b/Godeps/_workspace/src/github.com/gizak/termui/theme.go
new file mode 100644
index 000000000..c8ad94756
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/theme.go
@@ -0,0 +1,84 @@
+// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+// A ColorScheme represents the current look-and-feel of the dashboard.
+type ColorScheme struct {
+ BodyBg Attribute
+ BlockBg Attribute
+ HasBorder bool
+ BorderFg Attribute
+ BorderBg Attribute
+ BorderLabelTextFg Attribute
+ BorderLabelTextBg Attribute
+ ParTextFg Attribute
+ ParTextBg Attribute
+ SparklineLine Attribute
+ SparklineTitle Attribute
+ GaugeBar Attribute
+ GaugePercent Attribute
+ LineChartLine Attribute
+ LineChartAxes Attribute
+ ListItemFg Attribute
+ ListItemBg Attribute
+ BarChartBar Attribute
+ BarChartText Attribute
+ BarChartNum Attribute
+ MBarChartBar Attribute
+ MBarChartText Attribute
+ MBarChartNum Attribute
+}
+
+// default color scheme depends on the user's terminal setting.
+var themeDefault = ColorScheme{HasBorder: true}
+
+var themeHelloWorld = ColorScheme{
+ BodyBg: ColorBlack,
+ BlockBg: ColorBlack,
+ HasBorder: true,
+ BorderFg: ColorWhite,
+ BorderBg: ColorBlack,
+ BorderLabelTextBg: ColorBlack,
+ BorderLabelTextFg: ColorGreen,
+ ParTextBg: ColorBlack,
+ ParTextFg: ColorWhite,
+ SparklineLine: ColorMagenta,
+ SparklineTitle: ColorWhite,
+ GaugeBar: ColorRed,
+ GaugePercent: ColorWhite,
+ LineChartLine: ColorYellow | AttrBold,
+ LineChartAxes: ColorWhite,
+ ListItemBg: ColorBlack,
+ ListItemFg: ColorYellow,
+ BarChartBar: ColorRed,
+ BarChartNum: ColorWhite,
+ BarChartText: ColorCyan,
+ MBarChartBar: ColorRed,
+ MBarChartNum: ColorWhite,
+ MBarChartText: ColorCyan,
+}
+
+var theme = themeDefault // global dep
+
+// Theme returns the currently used theme.
+func Theme() ColorScheme {
+ return theme
+}
+
+// SetTheme sets a new, custom theme.
+func SetTheme(newTheme ColorScheme) {
+ theme = newTheme
+}
+
+// UseTheme sets a predefined scheme. Currently available: "hello-world" and
+// "black-and-white".
+func UseTheme(th string) {
+ switch th {
+ case "helloworld":
+ theme = themeHelloWorld
+ default:
+ theme = themeDefault
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/.gitignore b/Godeps/_workspace/src/github.com/howeyc/fsnotify/.gitignore
deleted file mode 100644
index e4706a9e9..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-# Setup a Global .gitignore for OS and editor generated files:
-# https://help.github.com/articles/ignoring-files
-# git config --global core.excludesfile ~/.gitignore_global
-
-.vagrant
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/AUTHORS b/Godeps/_workspace/src/github.com/howeyc/fsnotify/AUTHORS
deleted file mode 100644
index e52b72f83..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/AUTHORS
+++ /dev/null
@@ -1,28 +0,0 @@
-# Names should be added to this file as
-# Name or Organization <email address>
-# The email address is not required for organizations.
-
-# You can update this list using the following command:
-#
-# $ git shortlog -se | awk '{print $2 " " $3 " " $4}'
-
-# Please keep the list sorted.
-
-Adrien Bustany <adrien@bustany.org>
-Caleb Spare <cespare@gmail.com>
-Case Nelson <case@teammating.com>
-Chris Howey <howeyc@gmail.com> <chris@howey.me>
-Christoffer Buchholz <christoffer.buchholz@gmail.com>
-Dave Cheney <dave@cheney.net>
-Francisco Souza <f@souza.cc>
-John C Barstow
-Kelvin Fo <vmirage@gmail.com>
-Nathan Youngman <git@nathany.com>
-Paul Hammond <paul@paulhammond.org>
-Pursuit92 <JoshChase@techpursuit.net>
-Rob Figueiredo <robfig@gmail.com>
-Travis Cline <travis.cline@gmail.com>
-Tudor Golubenco <tudor.g@gmail.com>
-bronze1man <bronze1man@gmail.com>
-debrando <denis.brandolini@gmail.com>
-henrikedwards <henrik.edwards@gmail.com>
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/CHANGELOG.md b/Godeps/_workspace/src/github.com/howeyc/fsnotify/CHANGELOG.md
deleted file mode 100644
index 761686aa9..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/CHANGELOG.md
+++ /dev/null
@@ -1,160 +0,0 @@
-# Changelog
-
-## v0.9.0 / 2014-01-17
-
-* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany)
-* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare)
-* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library.
-
-## v0.8.12 / 2013-11-13
-
-* [API] Remove FD_SET and friends from Linux adapter
-
-## v0.8.11 / 2013-11-02
-
-* [Doc] Add Changelog [#72][] (thanks @nathany)
-* [Doc] Spotlight and double modify events on OS X [#62][] (reported by @paulhammond)
-
-## v0.8.10 / 2013-10-19
-
-* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott)
-* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer)
-* [Doc] specify OS-specific limits in README (thanks @debrando)
-
-## v0.8.9 / 2013-09-08
-
-* [Doc] Contributing (thanks @nathany)
-* [Doc] update package path in example code [#63][] (thanks @paulhammond)
-* [Doc] GoCI badge in README (Linux only) [#60][]
-* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany)
-
-## v0.8.8 / 2013-06-17
-
-* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie)
-
-## v0.8.7 / 2013-06-03
-
-* [API] Make syscall flags internal
-* [Fix] inotify: ignore event changes
-* [Fix] race in symlink test [#45][] (reported by @srid)
-* [Fix] tests on Windows
-* lower case error messages
-
-## v0.8.6 / 2013-05-23
-
-* kqueue: Use EVT_ONLY flag on Darwin
-* [Doc] Update README with full example
-
-## v0.8.5 / 2013-05-09
-
-* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg)
-
-## v0.8.4 / 2013-04-07
-
-* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz)
-
-## v0.8.3 / 2013-03-13
-
-* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin)
-* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin)
-
-## v0.8.2 / 2013-02-07
-
-* [Doc] add Authors
-* [Fix] fix data races for map access [#29][] (thanks @fsouza)
-
-## v0.8.1 / 2013-01-09
-
-* [Fix] Windows path separators
-* [Doc] BSD License
-
-## v0.8.0 / 2012-11-09
-
-* kqueue: directory watching improvements (thanks @vmirage)
-* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto)
-* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr)
-
-## v0.7.4 / 2012-10-09
-
-* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji)
-* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig)
-* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig)
-* [Fix] kqueue: modify after recreation of file
-
-## v0.7.3 / 2012-09-27
-
-* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage)
-* [Fix] kqueue: no longer get duplicate CREATE events
-
-## v0.7.2 / 2012-09-01
-
-* kqueue: events for created directories
-
-## v0.7.1 / 2012-07-14
-
-* [Fix] for renaming files
-
-## v0.7.0 / 2012-07-02
-
-* [Feature] FSNotify flags
-* [Fix] inotify: Added file name back to event path
-
-## v0.6.0 / 2012-06-06
-
-* kqueue: watch files after directory created (thanks @tmc)
-
-## v0.5.1 / 2012-05-22
-
-* [Fix] inotify: remove all watches before Close()
-
-## v0.5.0 / 2012-05-03
-
-* [API] kqueue: return errors during watch instead of sending over channel
-* kqueue: match symlink behavior on Linux
-* inotify: add `DELETE_SELF` (requested by @taralx)
-* [Fix] kqueue: handle EINTR (reported by @robfig)
-* [Doc] Godoc example [#1][] (thanks @davecheney)
-
-## v0.4.0 / 2012-03-30
-
-* Go 1 released: build with go tool
-* [Feature] Windows support using winfsnotify
-* Windows does not have attribute change notifications
-* Roll attribute notifications into IsModify
-
-## v0.3.0 / 2012-02-19
-
-* kqueue: add files when watch directory
-
-## v0.2.0 / 2011-12-30
-
-* update to latest Go weekly code
-
-## v0.1.0 / 2011-10-19
-
-* kqueue: add watch on file creation to match inotify
-* kqueue: create file event
-* inotify: ignore `IN_IGNORED` events
-* event String()
-* linux: common FileEvent functions
-* initial commit
-
-[#79]: https://github.com/howeyc/fsnotify/pull/79
-[#77]: https://github.com/howeyc/fsnotify/pull/77
-[#72]: https://github.com/howeyc/fsnotify/issues/72
-[#71]: https://github.com/howeyc/fsnotify/issues/71
-[#70]: https://github.com/howeyc/fsnotify/issues/70
-[#63]: https://github.com/howeyc/fsnotify/issues/63
-[#62]: https://github.com/howeyc/fsnotify/issues/62
-[#60]: https://github.com/howeyc/fsnotify/issues/60
-[#59]: https://github.com/howeyc/fsnotify/issues/59
-[#49]: https://github.com/howeyc/fsnotify/issues/49
-[#45]: https://github.com/howeyc/fsnotify/issues/45
-[#40]: https://github.com/howeyc/fsnotify/issues/40
-[#36]: https://github.com/howeyc/fsnotify/issues/36
-[#33]: https://github.com/howeyc/fsnotify/issues/33
-[#29]: https://github.com/howeyc/fsnotify/issues/29
-[#25]: https://github.com/howeyc/fsnotify/issues/25
-[#24]: https://github.com/howeyc/fsnotify/issues/24
-[#21]: https://github.com/howeyc/fsnotify/issues/21
-[#1]: https://github.com/howeyc/fsnotify/issues/1
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/CONTRIBUTING.md b/Godeps/_workspace/src/github.com/howeyc/fsnotify/CONTRIBUTING.md
deleted file mode 100644
index b2025d72c..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/CONTRIBUTING.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Contributing
-
-## Moving Notice
-
-There is a fork being actively developed with a new API in preparation for the Go Standard Library:
-[github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify)
-
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/LICENSE b/Godeps/_workspace/src/github.com/howeyc/fsnotify/LICENSE
deleted file mode 100644
index f21e54080..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/LICENSE
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright (c) 2012 The Go Authors. All rights reserved.
-Copyright (c) 2012 fsnotify Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/README.md b/Godeps/_workspace/src/github.com/howeyc/fsnotify/README.md
deleted file mode 100644
index 4c7498d38..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/README.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# File system notifications for Go
-
-[![GoDoc](https://godoc.org/github.com/howeyc/fsnotify?status.png)](http://godoc.org/github.com/howeyc/fsnotify)
-
-Cross platform: Windows, Linux, BSD and OS X.
-
-## Moving Notice
-
-There is a fork being actively developed with a new API in preparation for the Go Standard Library:
-[github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify)
-
-## Example:
-
-```go
-package main
-
-import (
- "log"
-
- "github.com/howeyc/fsnotify"
-)
-
-func main() {
- watcher, err := fsnotify.NewWatcher()
- if err != nil {
- log.Fatal(err)
- }
-
- done := make(chan bool)
-
- // Process events
- go func() {
- for {
- select {
- case ev := <-watcher.Event:
- log.Println("event:", ev)
- case err := <-watcher.Error:
- log.Println("error:", err)
- }
- }
- }()
-
- err = watcher.Watch("testDir")
- if err != nil {
- log.Fatal(err)
- }
-
- <-done
-
- /* ... do stuff ... */
- watcher.Close()
-}
-```
-
-For each event:
-* Name
-* IsCreate()
-* IsDelete()
-* IsModify()
-* IsRename()
-
-## FAQ
-
-**When a file is moved to another directory is it still being watched?**
-
-No (it shouldn't be, unless you are watching where it was moved to).
-
-**When I watch a directory, are all subdirectories watched as well?**
-
-No, you must add watches for any directory you want to watch (a recursive watcher is in the works [#56][]).
-
-**Do I have to watch the Error and Event channels in a separate goroutine?**
-
-As of now, yes. Looking into making this single-thread friendly (see [#7][])
-
-**Why am I receiving multiple events for the same file on OS X?**
-
-Spotlight indexing on OS X can result in multiple events (see [#62][]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#54][]).
-
-**How many files can be watched at once?**
-
-There are OS-specific limits as to how many watches can be created:
-* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit,
-reaching this limit results in a "no space left on device" error.
-* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
-
-
-[#62]: https://github.com/howeyc/fsnotify/issues/62
-[#56]: https://github.com/howeyc/fsnotify/issues/56
-[#54]: https://github.com/howeyc/fsnotify/issues/54
-[#7]: https://github.com/howeyc/fsnotify/issues/7
-
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/example_test.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/example_test.go
deleted file mode 100644
index d3130e222..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/example_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package fsnotify_test
-
-import (
- "log"
-
- "github.com/howeyc/fsnotify"
-)
-
-func ExampleNewWatcher() {
- watcher, err := fsnotify.NewWatcher()
- if err != nil {
- log.Fatal(err)
- }
-
- go func() {
- for {
- select {
- case ev := <-watcher.Event:
- log.Println("event:", ev)
- case err := <-watcher.Error:
- log.Println("error:", err)
- }
- }
- }()
-
- err = watcher.Watch("/tmp/foo")
- if err != nil {
- log.Fatal(err)
- }
-}
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify.go
deleted file mode 100644
index 9a48d847d..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package fsnotify implements file system notification.
-package fsnotify
-
-import "fmt"
-
-const (
- FSN_CREATE = 1
- FSN_MODIFY = 2
- FSN_DELETE = 4
- FSN_RENAME = 8
-
- FSN_ALL = FSN_MODIFY | FSN_DELETE | FSN_RENAME | FSN_CREATE
-)
-
-// Purge events from interal chan to external chan if passes filter
-func (w *Watcher) purgeEvents() {
- for ev := range w.internalEvent {
- sendEvent := false
- w.fsnmut.Lock()
- fsnFlags := w.fsnFlags[ev.Name]
- w.fsnmut.Unlock()
-
- if (fsnFlags&FSN_CREATE == FSN_CREATE) && ev.IsCreate() {
- sendEvent = true
- }
-
- if (fsnFlags&FSN_MODIFY == FSN_MODIFY) && ev.IsModify() {
- sendEvent = true
- }
-
- if (fsnFlags&FSN_DELETE == FSN_DELETE) && ev.IsDelete() {
- sendEvent = true
- }
-
- if (fsnFlags&FSN_RENAME == FSN_RENAME) && ev.IsRename() {
- sendEvent = true
- }
-
- if sendEvent {
- w.Event <- ev
- }
-
- // If there's no file, then no more events for user
- // BSD must keep watch for internal use (watches DELETEs to keep track
- // what files exist for create events)
- if ev.IsDelete() {
- w.fsnmut.Lock()
- delete(w.fsnFlags, ev.Name)
- w.fsnmut.Unlock()
- }
- }
-
- close(w.Event)
-}
-
-// Watch a given file path
-func (w *Watcher) Watch(path string) error {
- return w.WatchFlags(path, FSN_ALL)
-}
-
-// Watch a given file path for a particular set of notifications (FSN_MODIFY etc.)
-func (w *Watcher) WatchFlags(path string, flags uint32) error {
- w.fsnmut.Lock()
- w.fsnFlags[path] = flags
- w.fsnmut.Unlock()
- return w.watch(path)
-}
-
-// Remove a watch on a file
-func (w *Watcher) RemoveWatch(path string) error {
- w.fsnmut.Lock()
- delete(w.fsnFlags, path)
- w.fsnmut.Unlock()
- return w.removeWatch(path)
-}
-
-// String formats the event e in the form
-// "filename: DELETE|MODIFY|..."
-func (e *FileEvent) String() string {
- var events string = ""
-
- if e.IsCreate() {
- events += "|" + "CREATE"
- }
-
- if e.IsDelete() {
- events += "|" + "DELETE"
- }
-
- if e.IsModify() {
- events += "|" + "MODIFY"
- }
-
- if e.IsRename() {
- events += "|" + "RENAME"
- }
-
- if e.IsAttrib() {
- events += "|" + "ATTRIB"
- }
-
- if len(events) > 0 {
- events = events[1:]
- }
-
- return fmt.Sprintf("%q: %s", e.Name, events)
-}
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_bsd.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_bsd.go
deleted file mode 100644
index e6ffd7e5b..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_bsd.go
+++ /dev/null
@@ -1,496 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build freebsd openbsd netbsd darwin
-
-package fsnotify
-
-import (
- "errors"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "sync"
- "syscall"
-)
-
-const (
- // Flags (from <sys/event.h>)
- sys_NOTE_DELETE = 0x0001 /* vnode was removed */
- sys_NOTE_WRITE = 0x0002 /* data contents changed */
- sys_NOTE_EXTEND = 0x0004 /* size increased */
- sys_NOTE_ATTRIB = 0x0008 /* attributes changed */
- sys_NOTE_LINK = 0x0010 /* link count changed */
- sys_NOTE_RENAME = 0x0020 /* vnode was renamed */
- sys_NOTE_REVOKE = 0x0040 /* vnode access was revoked */
-
- // Watch all events
- sys_NOTE_ALLEVENTS = sys_NOTE_DELETE | sys_NOTE_WRITE | sys_NOTE_ATTRIB | sys_NOTE_RENAME
-
- // Block for 100 ms on each call to kevent
- keventWaitTime = 100e6
-)
-
-type FileEvent struct {
- mask uint32 // Mask of events
- Name string // File name (optional)
- create bool // set by fsnotify package if found new file
-}
-
-// IsCreate reports whether the FileEvent was triggered by a creation
-func (e *FileEvent) IsCreate() bool { return e.create }
-
-// IsDelete reports whether the FileEvent was triggered by a delete
-func (e *FileEvent) IsDelete() bool { return (e.mask & sys_NOTE_DELETE) == sys_NOTE_DELETE }
-
-// IsModify reports whether the FileEvent was triggered by a file modification
-func (e *FileEvent) IsModify() bool {
- return ((e.mask&sys_NOTE_WRITE) == sys_NOTE_WRITE || (e.mask&sys_NOTE_ATTRIB) == sys_NOTE_ATTRIB)
-}
-
-// IsRename reports whether the FileEvent was triggered by a change name
-func (e *FileEvent) IsRename() bool { return (e.mask & sys_NOTE_RENAME) == sys_NOTE_RENAME }
-
-// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata.
-func (e *FileEvent) IsAttrib() bool {
- return (e.mask & sys_NOTE_ATTRIB) == sys_NOTE_ATTRIB
-}
-
-type Watcher struct {
- mu sync.Mutex // Mutex for the Watcher itself.
- kq int // File descriptor (as returned by the kqueue() syscall)
- watches map[string]int // Map of watched file descriptors (key: path)
- wmut sync.Mutex // Protects access to watches.
- fsnFlags map[string]uint32 // Map of watched files to flags used for filter
- fsnmut sync.Mutex // Protects access to fsnFlags.
- enFlags map[string]uint32 // Map of watched files to evfilt note flags used in kqueue
- enmut sync.Mutex // Protects access to enFlags.
- paths map[int]string // Map of watched paths (key: watch descriptor)
- finfo map[int]os.FileInfo // Map of file information (isDir, isReg; key: watch descriptor)
- pmut sync.Mutex // Protects access to paths and finfo.
- fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events)
- femut sync.Mutex // Protects access to fileExists.
- externalWatches map[string]bool // Map of watches added by user of the library.
- ewmut sync.Mutex // Protects access to externalWatches.
- Error chan error // Errors are sent on this channel
- internalEvent chan *FileEvent // Events are queued on this channel
- Event chan *FileEvent // Events are returned on this channel
- done chan bool // Channel for sending a "quit message" to the reader goroutine
- isClosed bool // Set to true when Close() is first called
-}
-
-// NewWatcher creates and returns a new kevent instance using kqueue(2)
-func NewWatcher() (*Watcher, error) {
- fd, errno := syscall.Kqueue()
- if fd == -1 {
- return nil, os.NewSyscallError("kqueue", errno)
- }
- w := &Watcher{
- kq: fd,
- watches: make(map[string]int),
- fsnFlags: make(map[string]uint32),
- enFlags: make(map[string]uint32),
- paths: make(map[int]string),
- finfo: make(map[int]os.FileInfo),
- fileExists: make(map[string]bool),
- externalWatches: make(map[string]bool),
- internalEvent: make(chan *FileEvent),
- Event: make(chan *FileEvent),
- Error: make(chan error),
- done: make(chan bool, 1),
- }
-
- go w.readEvents()
- go w.purgeEvents()
- return w, nil
-}
-
-// Close closes a kevent watcher instance
-// It sends a message to the reader goroutine to quit and removes all watches
-// associated with the kevent instance
-func (w *Watcher) Close() error {
- w.mu.Lock()
- if w.isClosed {
- w.mu.Unlock()
- return nil
- }
- w.isClosed = true
- w.mu.Unlock()
-
- // Send "quit" message to the reader goroutine
- w.done <- true
- w.wmut.Lock()
- ws := w.watches
- w.wmut.Unlock()
- for path := range ws {
- w.removeWatch(path)
- }
-
- return nil
-}
-
-// AddWatch adds path to the watched file set.
-// The flags are interpreted as described in kevent(2).
-func (w *Watcher) addWatch(path string, flags uint32) error {
- w.mu.Lock()
- if w.isClosed {
- w.mu.Unlock()
- return errors.New("kevent instance already closed")
- }
- w.mu.Unlock()
-
- watchDir := false
-
- w.wmut.Lock()
- watchfd, found := w.watches[path]
- w.wmut.Unlock()
- if !found {
- fi, errstat := os.Lstat(path)
- if errstat != nil {
- return errstat
- }
-
- // don't watch socket
- if fi.Mode()&os.ModeSocket == os.ModeSocket {
- return nil
- }
-
- // Follow Symlinks
- // Unfortunately, Linux can add bogus symlinks to watch list without
- // issue, and Windows can't do symlinks period (AFAIK). To maintain
- // consistency, we will act like everything is fine. There will simply
- // be no file events for broken symlinks.
- // Hence the returns of nil on errors.
- if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
- path, err := filepath.EvalSymlinks(path)
- if err != nil {
- return nil
- }
-
- fi, errstat = os.Lstat(path)
- if errstat != nil {
- return nil
- }
- }
-
- fd, errno := syscall.Open(path, open_FLAGS, 0700)
- if fd == -1 {
- return errno
- }
- watchfd = fd
-
- w.wmut.Lock()
- w.watches[path] = watchfd
- w.wmut.Unlock()
-
- w.pmut.Lock()
- w.paths[watchfd] = path
- w.finfo[watchfd] = fi
- w.pmut.Unlock()
- }
- // Watch the directory if it has not been watched before.
- w.pmut.Lock()
- w.enmut.Lock()
- if w.finfo[watchfd].IsDir() &&
- (flags&sys_NOTE_WRITE) == sys_NOTE_WRITE &&
- (!found || (w.enFlags[path]&sys_NOTE_WRITE) != sys_NOTE_WRITE) {
- watchDir = true
- }
- w.enmut.Unlock()
- w.pmut.Unlock()
-
- w.enmut.Lock()
- w.enFlags[path] = flags
- w.enmut.Unlock()
-
- var kbuf [1]syscall.Kevent_t
- watchEntry := &kbuf[0]
- watchEntry.Fflags = flags
- syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR)
- entryFlags := watchEntry.Flags
- success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil)
- if success == -1 {
- return errno
- } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR {
- return errors.New("kevent add error")
- }
-
- if watchDir {
- errdir := w.watchDirectoryFiles(path)
- if errdir != nil {
- return errdir
- }
- }
- return nil
-}
-
-// Watch adds path to the watched file set, watching all events.
-func (w *Watcher) watch(path string) error {
- w.ewmut.Lock()
- w.externalWatches[path] = true
- w.ewmut.Unlock()
- return w.addWatch(path, sys_NOTE_ALLEVENTS)
-}
-
-// RemoveWatch removes path from the watched file set.
-func (w *Watcher) removeWatch(path string) error {
- w.wmut.Lock()
- watchfd, ok := w.watches[path]
- w.wmut.Unlock()
- if !ok {
- return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path))
- }
- var kbuf [1]syscall.Kevent_t
- watchEntry := &kbuf[0]
- syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE)
- entryFlags := watchEntry.Flags
- success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil)
- if success == -1 {
- return os.NewSyscallError("kevent_rm_watch", errno)
- } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR {
- return errors.New("kevent rm error")
- }
- syscall.Close(watchfd)
- w.wmut.Lock()
- delete(w.watches, path)
- w.wmut.Unlock()
- w.enmut.Lock()
- delete(w.enFlags, path)
- w.enmut.Unlock()
- w.pmut.Lock()
- delete(w.paths, watchfd)
- fInfo := w.finfo[watchfd]
- delete(w.finfo, watchfd)
- w.pmut.Unlock()
-
- // Find all watched paths that are in this directory that are not external.
- if fInfo.IsDir() {
- var pathsToRemove []string
- w.pmut.Lock()
- for _, wpath := range w.paths {
- wdir, _ := filepath.Split(wpath)
- if filepath.Clean(wdir) == filepath.Clean(path) {
- w.ewmut.Lock()
- if !w.externalWatches[wpath] {
- pathsToRemove = append(pathsToRemove, wpath)
- }
- w.ewmut.Unlock()
- }
- }
- w.pmut.Unlock()
- for _, p := range pathsToRemove {
- // Since these are internal, not much sense in propagating error
- // to the user, as that will just confuse them with an error about
- // a path they did not explicitly watch themselves.
- w.removeWatch(p)
- }
- }
-
- return nil
-}
-
-// readEvents reads from the kqueue file descriptor, converts the
-// received events into Event objects and sends them via the Event channel
-func (w *Watcher) readEvents() {
- var (
- eventbuf [10]syscall.Kevent_t // Event buffer
- events []syscall.Kevent_t // Received events
- twait *syscall.Timespec // Time to block waiting for events
- n int // Number of events returned from kevent
- errno error // Syscall errno
- )
- events = eventbuf[0:0]
- twait = new(syscall.Timespec)
- *twait = syscall.NsecToTimespec(keventWaitTime)
-
- for {
- // See if there is a message on the "done" channel
- var done bool
- select {
- case done = <-w.done:
- default:
- }
-
- // If "done" message is received
- if done {
- errno := syscall.Close(w.kq)
- if errno != nil {
- w.Error <- os.NewSyscallError("close", errno)
- }
- close(w.internalEvent)
- close(w.Error)
- return
- }
-
- // Get new events
- if len(events) == 0 {
- n, errno = syscall.Kevent(w.kq, nil, eventbuf[:], twait)
-
- // EINTR is okay, basically the syscall was interrupted before
- // timeout expired.
- if errno != nil && errno != syscall.EINTR {
- w.Error <- os.NewSyscallError("kevent", errno)
- continue
- }
-
- // Received some events
- if n > 0 {
- events = eventbuf[0:n]
- }
- }
-
- // Flush the events we received to the events channel
- for len(events) > 0 {
- fileEvent := new(FileEvent)
- watchEvent := &events[0]
- fileEvent.mask = uint32(watchEvent.Fflags)
- w.pmut.Lock()
- fileEvent.Name = w.paths[int(watchEvent.Ident)]
- fileInfo := w.finfo[int(watchEvent.Ident)]
- w.pmut.Unlock()
- if fileInfo != nil && fileInfo.IsDir() && !fileEvent.IsDelete() {
- // Double check to make sure the directory exist. This can happen when
- // we do a rm -fr on a recursively watched folders and we receive a
- // modification event first but the folder has been deleted and later
- // receive the delete event
- if _, err := os.Lstat(fileEvent.Name); os.IsNotExist(err) {
- // mark is as delete event
- fileEvent.mask |= sys_NOTE_DELETE
- }
- }
-
- if fileInfo != nil && fileInfo.IsDir() && fileEvent.IsModify() && !fileEvent.IsDelete() {
- w.sendDirectoryChangeEvents(fileEvent.Name)
- } else {
- // Send the event on the events channel
- w.internalEvent <- fileEvent
- }
-
- // Move to next event
- events = events[1:]
-
- if fileEvent.IsRename() {
- w.removeWatch(fileEvent.Name)
- w.femut.Lock()
- delete(w.fileExists, fileEvent.Name)
- w.femut.Unlock()
- }
- if fileEvent.IsDelete() {
- w.removeWatch(fileEvent.Name)
- w.femut.Lock()
- delete(w.fileExists, fileEvent.Name)
- w.femut.Unlock()
-
- // Look for a file that may have overwritten this
- // (ie mv f1 f2 will delete f2 then create f2)
- fileDir, _ := filepath.Split(fileEvent.Name)
- fileDir = filepath.Clean(fileDir)
- w.wmut.Lock()
- _, found := w.watches[fileDir]
- w.wmut.Unlock()
- if found {
- // make sure the directory exist before we watch for changes. When we
- // do a recursive watch and perform rm -fr, the parent directory might
- // have gone missing, ignore the missing directory and let the
- // upcoming delete event remove the watch form the parent folder
- if _, err := os.Lstat(fileDir); !os.IsNotExist(err) {
- w.sendDirectoryChangeEvents(fileDir)
- }
- }
- }
- }
- }
-}
-
-func (w *Watcher) watchDirectoryFiles(dirPath string) error {
- // Get all files
- files, err := ioutil.ReadDir(dirPath)
- if err != nil {
- return err
- }
-
- // Search for new files
- for _, fileInfo := range files {
- filePath := filepath.Join(dirPath, fileInfo.Name())
-
- // Inherit fsnFlags from parent directory
- w.fsnmut.Lock()
- if flags, found := w.fsnFlags[dirPath]; found {
- w.fsnFlags[filePath] = flags
- } else {
- w.fsnFlags[filePath] = FSN_ALL
- }
- w.fsnmut.Unlock()
-
- if fileInfo.IsDir() == false {
- // Watch file to mimic linux fsnotify
- e := w.addWatch(filePath, sys_NOTE_ALLEVENTS)
- if e != nil {
- return e
- }
- } else {
- // If the user is currently watching directory
- // we want to preserve the flags used
- w.enmut.Lock()
- currFlags, found := w.enFlags[filePath]
- w.enmut.Unlock()
- var newFlags uint32 = sys_NOTE_DELETE
- if found {
- newFlags |= currFlags
- }
-
- // Linux gives deletes if not explicitly watching
- e := w.addWatch(filePath, newFlags)
- if e != nil {
- return e
- }
- }
- w.femut.Lock()
- w.fileExists[filePath] = true
- w.femut.Unlock()
- }
-
- return nil
-}
-
-// sendDirectoryEvents searches the directory for newly created files
-// and sends them over the event channel. This functionality is to have
-// the BSD version of fsnotify match linux fsnotify which provides a
-// create event for files created in a watched directory.
-func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
- // Get all files
- files, err := ioutil.ReadDir(dirPath)
- if err != nil {
- w.Error <- err
- }
-
- // Search for new files
- for _, fileInfo := range files {
- filePath := filepath.Join(dirPath, fileInfo.Name())
- w.femut.Lock()
- _, doesExist := w.fileExists[filePath]
- w.femut.Unlock()
- if !doesExist {
- // Inherit fsnFlags from parent directory
- w.fsnmut.Lock()
- if flags, found := w.fsnFlags[dirPath]; found {
- w.fsnFlags[filePath] = flags
- } else {
- w.fsnFlags[filePath] = FSN_ALL
- }
- w.fsnmut.Unlock()
-
- // Send create event
- fileEvent := new(FileEvent)
- fileEvent.Name = filePath
- fileEvent.create = true
- w.internalEvent <- fileEvent
- }
- w.femut.Lock()
- w.fileExists[filePath] = true
- w.femut.Unlock()
- }
- w.watchDirectoryFiles(dirPath)
-}
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_linux.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_linux.go
deleted file mode 100644
index 80ade879f..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_linux.go
+++ /dev/null
@@ -1,304 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build linux
-
-package fsnotify
-
-import (
- "errors"
- "fmt"
- "os"
- "strings"
- "sync"
- "syscall"
- "unsafe"
-)
-
-const (
- // Options for inotify_init() are not exported
- // sys_IN_CLOEXEC uint32 = syscall.IN_CLOEXEC
- // sys_IN_NONBLOCK uint32 = syscall.IN_NONBLOCK
-
- // Options for AddWatch
- sys_IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW
- sys_IN_ONESHOT uint32 = syscall.IN_ONESHOT
- sys_IN_ONLYDIR uint32 = syscall.IN_ONLYDIR
-
- // The "sys_IN_MASK_ADD" option is not exported, as AddWatch
- // adds it automatically, if there is already a watch for the given path
- // sys_IN_MASK_ADD uint32 = syscall.IN_MASK_ADD
-
- // Events
- sys_IN_ACCESS uint32 = syscall.IN_ACCESS
- sys_IN_ALL_EVENTS uint32 = syscall.IN_ALL_EVENTS
- sys_IN_ATTRIB uint32 = syscall.IN_ATTRIB
- sys_IN_CLOSE uint32 = syscall.IN_CLOSE
- sys_IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE
- sys_IN_CLOSE_WRITE uint32 = syscall.IN_CLOSE_WRITE
- sys_IN_CREATE uint32 = syscall.IN_CREATE
- sys_IN_DELETE uint32 = syscall.IN_DELETE
- sys_IN_DELETE_SELF uint32 = syscall.IN_DELETE_SELF
- sys_IN_MODIFY uint32 = syscall.IN_MODIFY
- sys_IN_MOVE uint32 = syscall.IN_MOVE
- sys_IN_MOVED_FROM uint32 = syscall.IN_MOVED_FROM
- sys_IN_MOVED_TO uint32 = syscall.IN_MOVED_TO
- sys_IN_MOVE_SELF uint32 = syscall.IN_MOVE_SELF
- sys_IN_OPEN uint32 = syscall.IN_OPEN
-
- sys_AGNOSTIC_EVENTS = sys_IN_MOVED_TO | sys_IN_MOVED_FROM | sys_IN_CREATE | sys_IN_ATTRIB | sys_IN_MODIFY | sys_IN_MOVE_SELF | sys_IN_DELETE | sys_IN_DELETE_SELF
-
- // Special events
- sys_IN_ISDIR uint32 = syscall.IN_ISDIR
- sys_IN_IGNORED uint32 = syscall.IN_IGNORED
- sys_IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW
- sys_IN_UNMOUNT uint32 = syscall.IN_UNMOUNT
-)
-
-type FileEvent struct {
- mask uint32 // Mask of events
- cookie uint32 // Unique cookie associating related events (for rename(2))
- Name string // File name (optional)
-}
-
-// IsCreate reports whether the FileEvent was triggered by a creation
-func (e *FileEvent) IsCreate() bool {
- return (e.mask&sys_IN_CREATE) == sys_IN_CREATE || (e.mask&sys_IN_MOVED_TO) == sys_IN_MOVED_TO
-}
-
-// IsDelete reports whether the FileEvent was triggered by a delete
-func (e *FileEvent) IsDelete() bool {
- return (e.mask&sys_IN_DELETE_SELF) == sys_IN_DELETE_SELF || (e.mask&sys_IN_DELETE) == sys_IN_DELETE
-}
-
-// IsModify reports whether the FileEvent was triggered by a file modification or attribute change
-func (e *FileEvent) IsModify() bool {
- return ((e.mask&sys_IN_MODIFY) == sys_IN_MODIFY || (e.mask&sys_IN_ATTRIB) == sys_IN_ATTRIB)
-}
-
-// IsRename reports whether the FileEvent was triggered by a change name
-func (e *FileEvent) IsRename() bool {
- return ((e.mask&sys_IN_MOVE_SELF) == sys_IN_MOVE_SELF || (e.mask&sys_IN_MOVED_FROM) == sys_IN_MOVED_FROM)
-}
-
-// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata.
-func (e *FileEvent) IsAttrib() bool {
- return (e.mask & sys_IN_ATTRIB) == sys_IN_ATTRIB
-}
-
-type watch struct {
- wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
- flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
-}
-
-type Watcher struct {
- mu sync.Mutex // Map access
- fd int // File descriptor (as returned by the inotify_init() syscall)
- watches map[string]*watch // Map of inotify watches (key: path)
- fsnFlags map[string]uint32 // Map of watched files to flags used for filter
- fsnmut sync.Mutex // Protects access to fsnFlags.
- paths map[int]string // Map of watched paths (key: watch descriptor)
- Error chan error // Errors are sent on this channel
- internalEvent chan *FileEvent // Events are queued on this channel
- Event chan *FileEvent // Events are returned on this channel
- done chan bool // Channel for sending a "quit message" to the reader goroutine
- isClosed bool // Set to true when Close() is first called
-}
-
-// NewWatcher creates and returns a new inotify instance using inotify_init(2)
-func NewWatcher() (*Watcher, error) {
- fd, errno := syscall.InotifyInit()
- if fd == -1 {
- return nil, os.NewSyscallError("inotify_init", errno)
- }
- w := &Watcher{
- fd: fd,
- watches: make(map[string]*watch),
- fsnFlags: make(map[string]uint32),
- paths: make(map[int]string),
- internalEvent: make(chan *FileEvent),
- Event: make(chan *FileEvent),
- Error: make(chan error),
- done: make(chan bool, 1),
- }
-
- go w.readEvents()
- go w.purgeEvents()
- return w, nil
-}
-
-// Close closes an inotify watcher instance
-// It sends a message to the reader goroutine to quit and removes all watches
-// associated with the inotify instance
-func (w *Watcher) Close() error {
- if w.isClosed {
- return nil
- }
- w.isClosed = true
-
- // Remove all watches
- for path := range w.watches {
- w.RemoveWatch(path)
- }
-
- // Send "quit" message to the reader goroutine
- w.done <- true
-
- return nil
-}
-
-// AddWatch adds path to the watched file set.
-// The flags are interpreted as described in inotify_add_watch(2).
-func (w *Watcher) addWatch(path string, flags uint32) error {
- if w.isClosed {
- return errors.New("inotify instance already closed")
- }
-
- w.mu.Lock()
- watchEntry, found := w.watches[path]
- w.mu.Unlock()
- if found {
- watchEntry.flags |= flags
- flags |= syscall.IN_MASK_ADD
- }
- wd, errno := syscall.InotifyAddWatch(w.fd, path, flags)
- if wd == -1 {
- return errno
- }
-
- w.mu.Lock()
- w.watches[path] = &watch{wd: uint32(wd), flags: flags}
- w.paths[wd] = path
- w.mu.Unlock()
-
- return nil
-}
-
-// Watch adds path to the watched file set, watching all events.
-func (w *Watcher) watch(path string) error {
- return w.addWatch(path, sys_AGNOSTIC_EVENTS)
-}
-
-// RemoveWatch removes path from the watched file set.
-func (w *Watcher) removeWatch(path string) error {
- w.mu.Lock()
- defer w.mu.Unlock()
- watch, ok := w.watches[path]
- if !ok {
- return errors.New(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path))
- }
- success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
- if success == -1 {
- return os.NewSyscallError("inotify_rm_watch", errno)
- }
- delete(w.watches, path)
- return nil
-}
-
-// readEvents reads from the inotify file descriptor, converts the
-// received events into Event objects and sends them via the Event channel
-func (w *Watcher) readEvents() {
- var (
- buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
- n int // Number of bytes read with read()
- errno error // Syscall errno
- )
-
- for {
- // See if there is a message on the "done" channel
- select {
- case <-w.done:
- syscall.Close(w.fd)
- close(w.internalEvent)
- close(w.Error)
- return
- default:
- }
-
- n, errno = syscall.Read(w.fd, buf[:])
-
- // If EOF is received
- if n == 0 {
- syscall.Close(w.fd)
- close(w.internalEvent)
- close(w.Error)
- return
- }
-
- if n < 0 {
- w.Error <- os.NewSyscallError("read", errno)
- continue
- }
- if n < syscall.SizeofInotifyEvent {
- w.Error <- errors.New("inotify: short read in readEvents()")
- continue
- }
-
- var offset uint32 = 0
- // We don't know how many events we just read into the buffer
- // While the offset points to at least one whole event...
- for offset <= uint32(n-syscall.SizeofInotifyEvent) {
- // Point "raw" to the event in the buffer
- raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
- event := new(FileEvent)
- event.mask = uint32(raw.Mask)
- event.cookie = uint32(raw.Cookie)
- nameLen := uint32(raw.Len)
- // If the event happened to the watched directory or the watched file, the kernel
- // doesn't append the filename to the event, but we would like to always fill the
- // the "Name" field with a valid filename. We retrieve the path of the watch from
- // the "paths" map.
- w.mu.Lock()
- event.Name = w.paths[int(raw.Wd)]
- w.mu.Unlock()
- watchedName := event.Name
- if nameLen > 0 {
- // Point "bytes" at the first byte of the filename
- bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
- // The filename is padded with NUL bytes. TrimRight() gets rid of those.
- event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
- }
-
- // Send the events that are not ignored on the events channel
- if !event.ignoreLinux() {
- // Setup FSNotify flags (inherit from directory watch)
- w.fsnmut.Lock()
- if _, fsnFound := w.fsnFlags[event.Name]; !fsnFound {
- if fsnFlags, watchFound := w.fsnFlags[watchedName]; watchFound {
- w.fsnFlags[event.Name] = fsnFlags
- } else {
- w.fsnFlags[event.Name] = FSN_ALL
- }
- }
- w.fsnmut.Unlock()
-
- w.internalEvent <- event
- }
-
- // Move to the next event in the buffer
- offset += syscall.SizeofInotifyEvent + nameLen
- }
- }
-}
-
-// Certain types of events can be "ignored" and not sent over the Event
-// channel. Such as events marked ignore by the kernel, or MODIFY events
-// against files that do not exist.
-func (e *FileEvent) ignoreLinux() bool {
- // Ignore anything the inotify API says to ignore
- if e.mask&sys_IN_IGNORED == sys_IN_IGNORED {
- return true
- }
-
- // If the event is not a DELETE or RENAME, the file must exist.
- // Otherwise the event is ignored.
- // *Note*: this was put in place because it was seen that a MODIFY
- // event was sent after the DELETE. This ignores that MODIFY and
- // assumes a DELETE will come or has come if the file doesn't exist.
- if !(e.IsDelete() || e.IsRename()) {
- _, statErr := os.Lstat(e.Name)
- return os.IsNotExist(statErr)
- }
- return false
-}
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_bsd.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_bsd.go
deleted file mode 100644
index 37ea998d0..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_bsd.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build freebsd openbsd netbsd
-
-package fsnotify
-
-import "syscall"
-
-const open_FLAGS = syscall.O_NONBLOCK | syscall.O_RDONLY
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_darwin.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_darwin.go
deleted file mode 100644
index d450318e6..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_darwin.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build darwin
-
-package fsnotify
-
-import "syscall"
-
-const open_FLAGS = syscall.O_EVTONLY
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_symlink_test.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_symlink_test.go
deleted file mode 100644
index 39061f844..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_symlink_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build freebsd openbsd netbsd darwin linux
-
-package fsnotify
-
-import (
- "os"
- "path/filepath"
- "testing"
- "time"
-)
-
-func TestFsnotifyFakeSymlink(t *testing.T) {
- watcher := newWatcher(t)
-
- // Create directory to watch
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- var errorsReceived counter
- // Receive errors on the error channel on a separate goroutine
- go func() {
- for errors := range watcher.Error {
- t.Logf("Received error: %s", errors)
- errorsReceived.increment()
- }
- }()
-
- // Count the CREATE events received
- var createEventsReceived, otherEventsReceived counter
- go func() {
- for ev := range watcher.Event {
- t.Logf("event received: %s", ev)
- if ev.IsCreate() {
- createEventsReceived.increment()
- } else {
- otherEventsReceived.increment()
- }
- }
- }()
-
- addWatch(t, watcher, testDir)
-
- if err := os.Symlink(filepath.Join(testDir, "zzz"), filepath.Join(testDir, "zzznew")); err != nil {
- t.Fatalf("Failed to create bogus symlink: %s", err)
- }
- t.Logf("Created bogus symlink")
-
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
- time.Sleep(500 * time.Millisecond)
-
- // Should not be error, just no events for broken links (watching nothing)
- if errorsReceived.value() > 0 {
- t.Fatal("fsnotify errors have been received.")
- }
- if otherEventsReceived.value() > 0 {
- t.Fatal("fsnotify other events received on the broken link")
- }
-
- // Except for 1 create event (for the link itself)
- if createEventsReceived.value() == 0 {
- t.Fatal("fsnotify create events were not received after 500 ms")
- }
- if createEventsReceived.value() > 1 {
- t.Fatal("fsnotify more create events received than expected")
- }
-
- // Try closing the fsnotify instance
- t.Log("calling Close()")
- watcher.Close()
-}
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_test.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_test.go
deleted file mode 100644
index 3f5a6487f..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_test.go
+++ /dev/null
@@ -1,1010 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package fsnotify
-
-import (
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "sync/atomic"
- "testing"
- "time"
-)
-
-// An atomic counter
-type counter struct {
- val int32
-}
-
-func (c *counter) increment() {
- atomic.AddInt32(&c.val, 1)
-}
-
-func (c *counter) value() int32 {
- return atomic.LoadInt32(&c.val)
-}
-
-func (c *counter) reset() {
- atomic.StoreInt32(&c.val, 0)
-}
-
-// tempMkdir makes a temporary directory
-func tempMkdir(t *testing.T) string {
- dir, err := ioutil.TempDir("", "fsnotify")
- if err != nil {
- t.Fatalf("failed to create test directory: %s", err)
- }
- return dir
-}
-
-// newWatcher initializes an fsnotify Watcher instance.
-func newWatcher(t *testing.T) *Watcher {
- watcher, err := NewWatcher()
- if err != nil {
- t.Fatalf("NewWatcher() failed: %s", err)
- }
- return watcher
-}
-
-// addWatch adds a watch for a directory
-func addWatch(t *testing.T, watcher *Watcher, dir string) {
- if err := watcher.Watch(dir); err != nil {
- t.Fatalf("watcher.Watch(%q) failed: %s", dir, err)
- }
-}
-
-func TestFsnotifyMultipleOperations(t *testing.T) {
- watcher := newWatcher(t)
-
- // Receive errors on the error channel on a separate goroutine
- go func() {
- for err := range watcher.Error {
- t.Fatalf("error received: %s", err)
- }
- }()
-
- // Create directory to watch
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- // Create directory that's not watched
- testDirToMoveFiles := tempMkdir(t)
- defer os.RemoveAll(testDirToMoveFiles)
-
- testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile")
- testFileRenamed := filepath.Join(testDirToMoveFiles, "TestFsnotifySeqRename.testfile")
-
- addWatch(t, watcher, testDir)
-
- // Receive events on the event channel on a separate goroutine
- eventstream := watcher.Event
- var createReceived, modifyReceived, deleteReceived, renameReceived counter
- done := make(chan bool)
- go func() {
- for event := range eventstream {
- // Only count relevant events
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
- t.Logf("event received: %s", event)
- if event.IsDelete() {
- deleteReceived.increment()
- }
- if event.IsModify() {
- modifyReceived.increment()
- }
- if event.IsCreate() {
- createReceived.increment()
- }
- if event.IsRename() {
- renameReceived.increment()
- }
- } else {
- t.Logf("unexpected event received: %s", event)
- }
- }
- done <- true
- }()
-
- // Create a file
- // This should add at least one event to the fsnotify event queue
- var f *os.File
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
-
- time.Sleep(time.Millisecond)
- f.WriteString("data")
- f.Sync()
- f.Close()
-
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
-
- if err := testRename(testFile, testFileRenamed); err != nil {
- t.Fatalf("rename failed: %s", err)
- }
-
- // Modify the file outside of the watched dir
- f, err = os.Open(testFileRenamed)
- if err != nil {
- t.Fatalf("open test renamed file failed: %s", err)
- }
- f.WriteString("data")
- f.Sync()
- f.Close()
-
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
-
- // Recreate the file that was moved
- f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Close()
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
-
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
- time.Sleep(500 * time.Millisecond)
- cReceived := createReceived.value()
- if cReceived != 2 {
- t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
- }
- mReceived := modifyReceived.value()
- if mReceived != 1 {
- t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1)
- }
- dReceived := deleteReceived.value()
- rReceived := renameReceived.value()
- if dReceived+rReceived != 1 {
- t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", rReceived+dReceived, 1)
- }
-
- // Try closing the fsnotify instance
- t.Log("calling Close()")
- watcher.Close()
- t.Log("waiting for the event channel to become closed...")
- select {
- case <-done:
- t.Log("event channel closed")
- case <-time.After(2 * time.Second):
- t.Fatal("event stream was not closed after 2 seconds")
- }
-}
-
-func TestFsnotifyMultipleCreates(t *testing.T) {
- watcher := newWatcher(t)
-
- // Receive errors on the error channel on a separate goroutine
- go func() {
- for err := range watcher.Error {
- t.Fatalf("error received: %s", err)
- }
- }()
-
- // Create directory to watch
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile")
-
- addWatch(t, watcher, testDir)
-
- // Receive events on the event channel on a separate goroutine
- eventstream := watcher.Event
- var createReceived, modifyReceived, deleteReceived counter
- done := make(chan bool)
- go func() {
- for event := range eventstream {
- // Only count relevant events
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
- t.Logf("event received: %s", event)
- if event.IsDelete() {
- deleteReceived.increment()
- }
- if event.IsCreate() {
- createReceived.increment()
- }
- if event.IsModify() {
- modifyReceived.increment()
- }
- } else {
- t.Logf("unexpected event received: %s", event)
- }
- }
- done <- true
- }()
-
- // Create a file
- // This should add at least one event to the fsnotify event queue
- var f *os.File
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
-
- time.Sleep(time.Millisecond)
- f.WriteString("data")
- f.Sync()
- f.Close()
-
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
-
- os.Remove(testFile)
-
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
-
- // Recreate the file
- f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Close()
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
-
- // Modify
- f, err = os.OpenFile(testFile, os.O_WRONLY, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
-
- time.Sleep(time.Millisecond)
- f.WriteString("data")
- f.Sync()
- f.Close()
-
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
-
- // Modify
- f, err = os.OpenFile(testFile, os.O_WRONLY, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
-
- time.Sleep(time.Millisecond)
- f.WriteString("data")
- f.Sync()
- f.Close()
-
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
-
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
- time.Sleep(500 * time.Millisecond)
- cReceived := createReceived.value()
- if cReceived != 2 {
- t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
- }
- mReceived := modifyReceived.value()
- if mReceived < 3 {
- t.Fatalf("incorrect number of modify events received after 500 ms (%d vs atleast %d)", mReceived, 3)
- }
- dReceived := deleteReceived.value()
- if dReceived != 1 {
- t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", dReceived, 1)
- }
-
- // Try closing the fsnotify instance
- t.Log("calling Close()")
- watcher.Close()
- t.Log("waiting for the event channel to become closed...")
- select {
- case <-done:
- t.Log("event channel closed")
- case <-time.After(2 * time.Second):
- t.Fatal("event stream was not closed after 2 seconds")
- }
-}
-
-func TestFsnotifyDirOnly(t *testing.T) {
- watcher := newWatcher(t)
-
- // Create directory to watch
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- // Create a file before watching directory
- // This should NOT add any events to the fsnotify event queue
- testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
- {
- var f *os.File
- f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
- f.Close()
- }
-
- addWatch(t, watcher, testDir)
-
- // Receive errors on the error channel on a separate goroutine
- go func() {
- for err := range watcher.Error {
- t.Fatalf("error received: %s", err)
- }
- }()
-
- testFile := filepath.Join(testDir, "TestFsnotifyDirOnly.testfile")
-
- // Receive events on the event channel on a separate goroutine
- eventstream := watcher.Event
- var createReceived, modifyReceived, deleteReceived counter
- done := make(chan bool)
- go func() {
- for event := range eventstream {
- // Only count relevant events
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileAlreadyExists) {
- t.Logf("event received: %s", event)
- if event.IsDelete() {
- deleteReceived.increment()
- }
- if event.IsModify() {
- modifyReceived.increment()
- }
- if event.IsCreate() {
- createReceived.increment()
- }
- } else {
- t.Logf("unexpected event received: %s", event)
- }
- }
- done <- true
- }()
-
- // Create a file
- // This should add at least one event to the fsnotify event queue
- var f *os.File
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
-
- time.Sleep(time.Millisecond)
- f.WriteString("data")
- f.Sync()
- f.Close()
-
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
-
- os.Remove(testFile)
- os.Remove(testFileAlreadyExists)
-
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
- time.Sleep(500 * time.Millisecond)
- cReceived := createReceived.value()
- if cReceived != 1 {
- t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 1)
- }
- mReceived := modifyReceived.value()
- if mReceived != 1 {
- t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1)
- }
- dReceived := deleteReceived.value()
- if dReceived != 2 {
- t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2)
- }
-
- // Try closing the fsnotify instance
- t.Log("calling Close()")
- watcher.Close()
- t.Log("waiting for the event channel to become closed...")
- select {
- case <-done:
- t.Log("event channel closed")
- case <-time.After(2 * time.Second):
- t.Fatal("event stream was not closed after 2 seconds")
- }
-}
-
-func TestFsnotifyDeleteWatchedDir(t *testing.T) {
- watcher := newWatcher(t)
- defer watcher.Close()
-
- // Create directory to watch
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- // Create a file before watching directory
- testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
- {
- var f *os.File
- f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
- f.Close()
- }
-
- addWatch(t, watcher, testDir)
-
- // Add a watch for testFile
- addWatch(t, watcher, testFileAlreadyExists)
-
- // Receive errors on the error channel on a separate goroutine
- go func() {
- for err := range watcher.Error {
- t.Fatalf("error received: %s", err)
- }
- }()
-
- // Receive events on the event channel on a separate goroutine
- eventstream := watcher.Event
- var deleteReceived counter
- go func() {
- for event := range eventstream {
- // Only count relevant events
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFileAlreadyExists) {
- t.Logf("event received: %s", event)
- if event.IsDelete() {
- deleteReceived.increment()
- }
- } else {
- t.Logf("unexpected event received: %s", event)
- }
- }
- }()
-
- os.RemoveAll(testDir)
-
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
- time.Sleep(500 * time.Millisecond)
- dReceived := deleteReceived.value()
- if dReceived < 2 {
- t.Fatalf("did not receive at least %d delete events, received %d after 500 ms", 2, dReceived)
- }
-}
-
-func TestFsnotifySubDir(t *testing.T) {
- watcher := newWatcher(t)
-
- // Create directory to watch
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- testFile1 := filepath.Join(testDir, "TestFsnotifyFile1.testfile")
- testSubDir := filepath.Join(testDir, "sub")
- testSubDirFile := filepath.Join(testDir, "sub/TestFsnotifyFile1.testfile")
-
- // Receive errors on the error channel on a separate goroutine
- go func() {
- for err := range watcher.Error {
- t.Fatalf("error received: %s", err)
- }
- }()
-
- // Receive events on the event channel on a separate goroutine
- eventstream := watcher.Event
- var createReceived, deleteReceived counter
- done := make(chan bool)
- go func() {
- for event := range eventstream {
- // Only count relevant events
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testSubDir) || event.Name == filepath.Clean(testFile1) {
- t.Logf("event received: %s", event)
- if event.IsCreate() {
- createReceived.increment()
- }
- if event.IsDelete() {
- deleteReceived.increment()
- }
- } else {
- t.Logf("unexpected event received: %s", event)
- }
- }
- done <- true
- }()
-
- addWatch(t, watcher, testDir)
-
- // Create sub-directory
- if err := os.Mkdir(testSubDir, 0777); err != nil {
- t.Fatalf("failed to create test sub-directory: %s", err)
- }
-
- // Create a file
- var f *os.File
- f, err := os.OpenFile(testFile1, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
- f.Close()
-
- // Create a file (Should not see this! we are not watching subdir)
- var fs *os.File
- fs, err = os.OpenFile(testSubDirFile, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- fs.Sync()
- fs.Close()
-
- time.Sleep(200 * time.Millisecond)
-
- // Make sure receive deletes for both file and sub-directory
- os.RemoveAll(testSubDir)
- os.Remove(testFile1)
-
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
- time.Sleep(500 * time.Millisecond)
- cReceived := createReceived.value()
- if cReceived != 2 {
- t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
- }
- dReceived := deleteReceived.value()
- if dReceived != 2 {
- t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2)
- }
-
- // Try closing the fsnotify instance
- t.Log("calling Close()")
- watcher.Close()
- t.Log("waiting for the event channel to become closed...")
- select {
- case <-done:
- t.Log("event channel closed")
- case <-time.After(2 * time.Second):
- t.Fatal("event stream was not closed after 2 seconds")
- }
-}
-
-func TestFsnotifyRename(t *testing.T) {
- watcher := newWatcher(t)
-
- // Create directory to watch
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- addWatch(t, watcher, testDir)
-
- // Receive errors on the error channel on a separate goroutine
- go func() {
- for err := range watcher.Error {
- t.Fatalf("error received: %s", err)
- }
- }()
-
- testFile := filepath.Join(testDir, "TestFsnotifyEvents.testfile")
- testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
-
- // Receive events on the event channel on a separate goroutine
- eventstream := watcher.Event
- var renameReceived counter
- done := make(chan bool)
- go func() {
- for event := range eventstream {
- // Only count relevant events
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) {
- if event.IsRename() {
- renameReceived.increment()
- }
- t.Logf("event received: %s", event)
- } else {
- t.Logf("unexpected event received: %s", event)
- }
- }
- done <- true
- }()
-
- // Create a file
- // This should add at least one event to the fsnotify event queue
- var f *os.File
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
-
- f.WriteString("data")
- f.Sync()
- f.Close()
-
- // Add a watch for testFile
- addWatch(t, watcher, testFile)
-
- if err := testRename(testFile, testFileRenamed); err != nil {
- t.Fatalf("rename failed: %s", err)
- }
-
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
- time.Sleep(500 * time.Millisecond)
- if renameReceived.value() == 0 {
- t.Fatal("fsnotify rename events have not been received after 500 ms")
- }
-
- // Try closing the fsnotify instance
- t.Log("calling Close()")
- watcher.Close()
- t.Log("waiting for the event channel to become closed...")
- select {
- case <-done:
- t.Log("event channel closed")
- case <-time.After(2 * time.Second):
- t.Fatal("event stream was not closed after 2 seconds")
- }
-
- os.Remove(testFileRenamed)
-}
-
-func TestFsnotifyRenameToCreate(t *testing.T) {
- watcher := newWatcher(t)
-
- // Create directory to watch
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- // Create directory to get file
- testDirFrom := tempMkdir(t)
- defer os.RemoveAll(testDirFrom)
-
- addWatch(t, watcher, testDir)
-
- // Receive errors on the error channel on a separate goroutine
- go func() {
- for err := range watcher.Error {
- t.Fatalf("error received: %s", err)
- }
- }()
-
- testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile")
- testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
-
- // Receive events on the event channel on a separate goroutine
- eventstream := watcher.Event
- var createReceived counter
- done := make(chan bool)
- go func() {
- for event := range eventstream {
- // Only count relevant events
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) {
- if event.IsCreate() {
- createReceived.increment()
- }
- t.Logf("event received: %s", event)
- } else {
- t.Logf("unexpected event received: %s", event)
- }
- }
- done <- true
- }()
-
- // Create a file
- // This should add at least one event to the fsnotify event queue
- var f *os.File
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
- f.Close()
-
- if err := testRename(testFile, testFileRenamed); err != nil {
- t.Fatalf("rename failed: %s", err)
- }
-
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
- time.Sleep(500 * time.Millisecond)
- if createReceived.value() == 0 {
- t.Fatal("fsnotify create events have not been received after 500 ms")
- }
-
- // Try closing the fsnotify instance
- t.Log("calling Close()")
- watcher.Close()
- t.Log("waiting for the event channel to become closed...")
- select {
- case <-done:
- t.Log("event channel closed")
- case <-time.After(2 * time.Second):
- t.Fatal("event stream was not closed after 2 seconds")
- }
-
- os.Remove(testFileRenamed)
-}
-
-func TestFsnotifyRenameToOverwrite(t *testing.T) {
- switch runtime.GOOS {
- case "plan9", "windows":
- t.Skipf("skipping test on %q (os.Rename over existing file does not create event).", runtime.GOOS)
- }
-
- watcher := newWatcher(t)
-
- // Create directory to watch
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- // Create directory to get file
- testDirFrom := tempMkdir(t)
- defer os.RemoveAll(testDirFrom)
-
- testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile")
- testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
-
- // Create a file
- var fr *os.File
- fr, err := os.OpenFile(testFileRenamed, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- fr.Sync()
- fr.Close()
-
- addWatch(t, watcher, testDir)
-
- // Receive errors on the error channel on a separate goroutine
- go func() {
- for err := range watcher.Error {
- t.Fatalf("error received: %s", err)
- }
- }()
-
- // Receive events on the event channel on a separate goroutine
- eventstream := watcher.Event
- var eventReceived counter
- done := make(chan bool)
- go func() {
- for event := range eventstream {
- // Only count relevant events
- if event.Name == filepath.Clean(testFileRenamed) {
- eventReceived.increment()
- t.Logf("event received: %s", event)
- } else {
- t.Logf("unexpected event received: %s", event)
- }
- }
- done <- true
- }()
-
- // Create a file
- // This should add at least one event to the fsnotify event queue
- var f *os.File
- f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
- f.Close()
-
- if err := testRename(testFile, testFileRenamed); err != nil {
- t.Fatalf("rename failed: %s", err)
- }
-
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
- time.Sleep(500 * time.Millisecond)
- if eventReceived.value() == 0 {
- t.Fatal("fsnotify events have not been received after 500 ms")
- }
-
- // Try closing the fsnotify instance
- t.Log("calling Close()")
- watcher.Close()
- t.Log("waiting for the event channel to become closed...")
- select {
- case <-done:
- t.Log("event channel closed")
- case <-time.After(2 * time.Second):
- t.Fatal("event stream was not closed after 2 seconds")
- }
-
- os.Remove(testFileRenamed)
-}
-
-func TestRemovalOfWatch(t *testing.T) {
- // Create directory to watch
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- // Create a file before watching directory
- testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
- {
- var f *os.File
- f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
- f.Close()
- }
-
- watcher := newWatcher(t)
- defer watcher.Close()
-
- addWatch(t, watcher, testDir)
- if err := watcher.RemoveWatch(testDir); err != nil {
- t.Fatalf("Could not remove the watch: %v\n", err)
- }
-
- go func() {
- select {
- case ev := <-watcher.Event:
- t.Fatalf("We received event: %v\n", ev)
- case <-time.After(500 * time.Millisecond):
- t.Log("No event received, as expected.")
- }
- }()
-
- time.Sleep(200 * time.Millisecond)
- // Modify the file outside of the watched dir
- f, err := os.Open(testFileAlreadyExists)
- if err != nil {
- t.Fatalf("Open test file failed: %s", err)
- }
- f.WriteString("data")
- f.Sync()
- f.Close()
- if err := os.Chmod(testFileAlreadyExists, 0700); err != nil {
- t.Fatalf("chmod failed: %s", err)
- }
- time.Sleep(400 * time.Millisecond)
-}
-
-func TestFsnotifyAttrib(t *testing.T) {
- if runtime.GOOS == "windows" {
- t.Skip("attributes don't work on Windows.")
- }
-
- watcher := newWatcher(t)
-
- // Create directory to watch
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- // Receive errors on the error channel on a separate goroutine
- go func() {
- for err := range watcher.Error {
- t.Fatalf("error received: %s", err)
- }
- }()
-
- testFile := filepath.Join(testDir, "TestFsnotifyAttrib.testfile")
-
- // Receive events on the event channel on a separate goroutine
- eventstream := watcher.Event
- // The modifyReceived counter counts IsModify events that are not IsAttrib,
- // and the attribReceived counts IsAttrib events (which are also IsModify as
- // a consequence).
- var modifyReceived counter
- var attribReceived counter
- done := make(chan bool)
- go func() {
- for event := range eventstream {
- // Only count relevant events
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
- if event.IsModify() {
- modifyReceived.increment()
- }
- if event.IsAttrib() {
- attribReceived.increment()
- }
- t.Logf("event received: %s", event)
- } else {
- t.Logf("unexpected event received: %s", event)
- }
- }
- done <- true
- }()
-
- // Create a file
- // This should add at least one event to the fsnotify event queue
- var f *os.File
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatalf("creating test file failed: %s", err)
- }
- f.Sync()
-
- f.WriteString("data")
- f.Sync()
- f.Close()
-
- // Add a watch for testFile
- addWatch(t, watcher, testFile)
-
- if err := os.Chmod(testFile, 0700); err != nil {
- t.Fatalf("chmod failed: %s", err)
- }
-
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
- // Creating/writing a file changes also the mtime, so IsAttrib should be set to true here
- time.Sleep(500 * time.Millisecond)
- if modifyReceived.value() == 0 {
- t.Fatal("fsnotify modify events have not received after 500 ms")
- }
- if attribReceived.value() == 0 {
- t.Fatal("fsnotify attribute events have not received after 500 ms")
- }
-
- // Modifying the contents of the file does not set the attrib flag (although eg. the mtime
- // might have been modified).
- modifyReceived.reset()
- attribReceived.reset()
-
- f, err = os.OpenFile(testFile, os.O_WRONLY, 0)
- if err != nil {
- t.Fatalf("reopening test file failed: %s", err)
- }
-
- f.WriteString("more data")
- f.Sync()
- f.Close()
-
- time.Sleep(500 * time.Millisecond)
-
- if modifyReceived.value() != 1 {
- t.Fatal("didn't receive a modify event after changing test file contents")
- }
-
- if attribReceived.value() != 0 {
- t.Fatal("did receive an unexpected attrib event after changing test file contents")
- }
-
- modifyReceived.reset()
- attribReceived.reset()
-
- // Doing a chmod on the file should trigger an event with the "attrib" flag set (the contents
- // of the file are not changed though)
- if err := os.Chmod(testFile, 0600); err != nil {
- t.Fatalf("chmod failed: %s", err)
- }
-
- time.Sleep(500 * time.Millisecond)
-
- if attribReceived.value() != 1 {
- t.Fatal("didn't receive an attribute change after 500ms")
- }
-
- // Try closing the fsnotify instance
- t.Log("calling Close()")
- watcher.Close()
- t.Log("waiting for the event channel to become closed...")
- select {
- case <-done:
- t.Log("event channel closed")
- case <-time.After(1e9):
- t.Fatal("event stream was not closed after 1 second")
- }
-
- os.Remove(testFile)
-}
-
-func TestFsnotifyClose(t *testing.T) {
- watcher := newWatcher(t)
- watcher.Close()
-
- var done int32
- go func() {
- watcher.Close()
- atomic.StoreInt32(&done, 1)
- }()
-
- time.Sleep(50e6) // 50 ms
- if atomic.LoadInt32(&done) == 0 {
- t.Fatal("double Close() test failed: second Close() call didn't return")
- }
-
- testDir := tempMkdir(t)
- defer os.RemoveAll(testDir)
-
- if err := watcher.Watch(testDir); err == nil {
- t.Fatal("expected error on Watch() after Close(), got nil")
- }
-}
-
-func testRename(file1, file2 string) error {
- switch runtime.GOOS {
- case "windows", "plan9":
- return os.Rename(file1, file2)
- default:
- cmd := exec.Command("mv", file1, file2)
- return cmd.Run()
- }
-}
diff --git a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_windows.go b/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_windows.go
deleted file mode 100644
index d88ae6340..000000000
--- a/Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_windows.go
+++ /dev/null
@@ -1,598 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build windows
-
-package fsnotify
-
-import (
- "errors"
- "fmt"
- "os"
- "path/filepath"
- "runtime"
- "sync"
- "syscall"
- "unsafe"
-)
-
-const (
- // Options for AddWatch
- sys_FS_ONESHOT = 0x80000000
- sys_FS_ONLYDIR = 0x1000000
-
- // Events
- sys_FS_ACCESS = 0x1
- sys_FS_ALL_EVENTS = 0xfff
- sys_FS_ATTRIB = 0x4
- sys_FS_CLOSE = 0x18
- sys_FS_CREATE = 0x100
- sys_FS_DELETE = 0x200
- sys_FS_DELETE_SELF = 0x400
- sys_FS_MODIFY = 0x2
- sys_FS_MOVE = 0xc0
- sys_FS_MOVED_FROM = 0x40
- sys_FS_MOVED_TO = 0x80
- sys_FS_MOVE_SELF = 0x800
-
- // Special events
- sys_FS_IGNORED = 0x8000
- sys_FS_Q_OVERFLOW = 0x4000
-)
-
-const (
- // TODO(nj): Use syscall.ERROR_MORE_DATA from ztypes_windows in Go 1.3+
- sys_ERROR_MORE_DATA syscall.Errno = 234
-)
-
-// Event is the type of the notification messages
-// received on the watcher's Event channel.
-type FileEvent struct {
- mask uint32 // Mask of events
- cookie uint32 // Unique cookie associating related events (for rename)
- Name string // File name (optional)
-}
-
-// IsCreate reports whether the FileEvent was triggered by a creation
-func (e *FileEvent) IsCreate() bool { return (e.mask & sys_FS_CREATE) == sys_FS_CREATE }
-
-// IsDelete reports whether the FileEvent was triggered by a delete
-func (e *FileEvent) IsDelete() bool {
- return ((e.mask&sys_FS_DELETE) == sys_FS_DELETE || (e.mask&sys_FS_DELETE_SELF) == sys_FS_DELETE_SELF)
-}
-
-// IsModify reports whether the FileEvent was triggered by a file modification or attribute change
-func (e *FileEvent) IsModify() bool {
- return ((e.mask&sys_FS_MODIFY) == sys_FS_MODIFY || (e.mask&sys_FS_ATTRIB) == sys_FS_ATTRIB)
-}
-
-// IsRename reports whether the FileEvent was triggered by a change name
-func (e *FileEvent) IsRename() bool {
- return ((e.mask&sys_FS_MOVE) == sys_FS_MOVE || (e.mask&sys_FS_MOVE_SELF) == sys_FS_MOVE_SELF || (e.mask&sys_FS_MOVED_FROM) == sys_FS_MOVED_FROM || (e.mask&sys_FS_MOVED_TO) == sys_FS_MOVED_TO)
-}
-
-// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata.
-func (e *FileEvent) IsAttrib() bool {
- return (e.mask & sys_FS_ATTRIB) == sys_FS_ATTRIB
-}
-
-const (
- opAddWatch = iota
- opRemoveWatch
-)
-
-const (
- provisional uint64 = 1 << (32 + iota)
-)
-
-type input struct {
- op int
- path string
- flags uint32
- reply chan error
-}
-
-type inode struct {
- handle syscall.Handle
- volume uint32
- index uint64
-}
-
-type watch struct {
- ov syscall.Overlapped
- ino *inode // i-number
- path string // Directory path
- mask uint64 // Directory itself is being watched with these notify flags
- names map[string]uint64 // Map of names being watched and their notify flags
- rename string // Remembers the old name while renaming a file
- buf [4096]byte
-}
-
-type indexMap map[uint64]*watch
-type watchMap map[uint32]indexMap
-
-// A Watcher waits for and receives event notifications
-// for a specific set of files and directories.
-type Watcher struct {
- mu sync.Mutex // Map access
- port syscall.Handle // Handle to completion port
- watches watchMap // Map of watches (key: i-number)
- fsnFlags map[string]uint32 // Map of watched files to flags used for filter
- fsnmut sync.Mutex // Protects access to fsnFlags.
- input chan *input // Inputs to the reader are sent on this channel
- internalEvent chan *FileEvent // Events are queued on this channel
- Event chan *FileEvent // Events are returned on this channel
- Error chan error // Errors are sent on this channel
- isClosed bool // Set to true when Close() is first called
- quit chan chan<- error
- cookie uint32
-}
-
-// NewWatcher creates and returns a Watcher.
-func NewWatcher() (*Watcher, error) {
- port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
- if e != nil {
- return nil, os.NewSyscallError("CreateIoCompletionPort", e)
- }
- w := &Watcher{
- port: port,
- watches: make(watchMap),
- fsnFlags: make(map[string]uint32),
- input: make(chan *input, 1),
- Event: make(chan *FileEvent, 50),
- internalEvent: make(chan *FileEvent),
- Error: make(chan error),
- quit: make(chan chan<- error, 1),
- }
- go w.readEvents()
- go w.purgeEvents()
- return w, nil
-}
-
-// Close closes a Watcher.
-// It sends a message to the reader goroutine to quit and removes all watches
-// associated with the watcher.
-func (w *Watcher) Close() error {
- if w.isClosed {
- return nil
- }
- w.isClosed = true
-
- // Send "quit" message to the reader goroutine
- ch := make(chan error)
- w.quit <- ch
- if err := w.wakeupReader(); err != nil {
- return err
- }
- return <-ch
-}
-
-// AddWatch adds path to the watched file set.
-func (w *Watcher) AddWatch(path string, flags uint32) error {
- if w.isClosed {
- return errors.New("watcher already closed")
- }
- in := &input{
- op: opAddWatch,
- path: filepath.Clean(path),
- flags: flags,
- reply: make(chan error),
- }
- w.input <- in
- if err := w.wakeupReader(); err != nil {
- return err
- }
- return <-in.reply
-}
-
-// Watch adds path to the watched file set, watching all events.
-func (w *Watcher) watch(path string) error {
- return w.AddWatch(path, sys_FS_ALL_EVENTS)
-}
-
-// RemoveWatch removes path from the watched file set.
-func (w *Watcher) removeWatch(path string) error {
- in := &input{
- op: opRemoveWatch,
- path: filepath.Clean(path),
- reply: make(chan error),
- }
- w.input <- in
- if err := w.wakeupReader(); err != nil {
- return err
- }
- return <-in.reply
-}
-
-func (w *Watcher) wakeupReader() error {
- e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
- if e != nil {
- return os.NewSyscallError("PostQueuedCompletionStatus", e)
- }
- return nil
-}
-
-func getDir(pathname string) (dir string, err error) {
- attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
- if e != nil {
- return "", os.NewSyscallError("GetFileAttributes", e)
- }
- if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
- dir = pathname
- } else {
- dir, _ = filepath.Split(pathname)
- dir = filepath.Clean(dir)
- }
- return
-}
-
-func getIno(path string) (ino *inode, err error) {
- h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
- syscall.FILE_LIST_DIRECTORY,
- syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
- nil, syscall.OPEN_EXISTING,
- syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
- if e != nil {
- return nil, os.NewSyscallError("CreateFile", e)
- }
- var fi syscall.ByHandleFileInformation
- if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
- syscall.CloseHandle(h)
- return nil, os.NewSyscallError("GetFileInformationByHandle", e)
- }
- ino = &inode{
- handle: h,
- volume: fi.VolumeSerialNumber,
- index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
- }
- return ino, nil
-}
-
-// Must run within the I/O thread.
-func (m watchMap) get(ino *inode) *watch {
- if i := m[ino.volume]; i != nil {
- return i[ino.index]
- }
- return nil
-}
-
-// Must run within the I/O thread.
-func (m watchMap) set(ino *inode, watch *watch) {
- i := m[ino.volume]
- if i == nil {
- i = make(indexMap)
- m[ino.volume] = i
- }
- i[ino.index] = watch
-}
-
-// Must run within the I/O thread.
-func (w *Watcher) addWatch(pathname string, flags uint64) error {
- dir, err := getDir(pathname)
- if err != nil {
- return err
- }
- if flags&sys_FS_ONLYDIR != 0 && pathname != dir {
- return nil
- }
- ino, err := getIno(dir)
- if err != nil {
- return err
- }
- w.mu.Lock()
- watchEntry := w.watches.get(ino)
- w.mu.Unlock()
- if watchEntry == nil {
- if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
- syscall.CloseHandle(ino.handle)
- return os.NewSyscallError("CreateIoCompletionPort", e)
- }
- watchEntry = &watch{
- ino: ino,
- path: dir,
- names: make(map[string]uint64),
- }
- w.mu.Lock()
- w.watches.set(ino, watchEntry)
- w.mu.Unlock()
- flags |= provisional
- } else {
- syscall.CloseHandle(ino.handle)
- }
- if pathname == dir {
- watchEntry.mask |= flags
- } else {
- watchEntry.names[filepath.Base(pathname)] |= flags
- }
- if err = w.startRead(watchEntry); err != nil {
- return err
- }
- if pathname == dir {
- watchEntry.mask &= ^provisional
- } else {
- watchEntry.names[filepath.Base(pathname)] &= ^provisional
- }
- return nil
-}
-
-// Must run within the I/O thread.
-func (w *Watcher) remWatch(pathname string) error {
- dir, err := getDir(pathname)
- if err != nil {
- return err
- }
- ino, err := getIno(dir)
- if err != nil {
- return err
- }
- w.mu.Lock()
- watch := w.watches.get(ino)
- w.mu.Unlock()
- if watch == nil {
- return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
- }
- if pathname == dir {
- w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED)
- watch.mask = 0
- } else {
- name := filepath.Base(pathname)
- w.sendEvent(watch.path+"\\"+name, watch.names[name]&sys_FS_IGNORED)
- delete(watch.names, name)
- }
- return w.startRead(watch)
-}
-
-// Must run within the I/O thread.
-func (w *Watcher) deleteWatch(watch *watch) {
- for name, mask := range watch.names {
- if mask&provisional == 0 {
- w.sendEvent(watch.path+"\\"+name, mask&sys_FS_IGNORED)
- }
- delete(watch.names, name)
- }
- if watch.mask != 0 {
- if watch.mask&provisional == 0 {
- w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED)
- }
- watch.mask = 0
- }
-}
-
-// Must run within the I/O thread.
-func (w *Watcher) startRead(watch *watch) error {
- if e := syscall.CancelIo(watch.ino.handle); e != nil {
- w.Error <- os.NewSyscallError("CancelIo", e)
- w.deleteWatch(watch)
- }
- mask := toWindowsFlags(watch.mask)
- for _, m := range watch.names {
- mask |= toWindowsFlags(m)
- }
- if mask == 0 {
- if e := syscall.CloseHandle(watch.ino.handle); e != nil {
- w.Error <- os.NewSyscallError("CloseHandle", e)
- }
- w.mu.Lock()
- delete(w.watches[watch.ino.volume], watch.ino.index)
- w.mu.Unlock()
- return nil
- }
- e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
- uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
- if e != nil {
- err := os.NewSyscallError("ReadDirectoryChanges", e)
- if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
- // Watched directory was probably removed
- if w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) {
- if watch.mask&sys_FS_ONESHOT != 0 {
- watch.mask = 0
- }
- }
- err = nil
- }
- w.deleteWatch(watch)
- w.startRead(watch)
- return err
- }
- return nil
-}
-
-// readEvents reads from the I/O completion port, converts the
-// received events into Event objects and sends them via the Event channel.
-// Entry point to the I/O thread.
-func (w *Watcher) readEvents() {
- var (
- n, key uint32
- ov *syscall.Overlapped
- )
- runtime.LockOSThread()
-
- for {
- e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
- watch := (*watch)(unsafe.Pointer(ov))
-
- if watch == nil {
- select {
- case ch := <-w.quit:
- w.mu.Lock()
- var indexes []indexMap
- for _, index := range w.watches {
- indexes = append(indexes, index)
- }
- w.mu.Unlock()
- for _, index := range indexes {
- for _, watch := range index {
- w.deleteWatch(watch)
- w.startRead(watch)
- }
- }
- var err error
- if e := syscall.CloseHandle(w.port); e != nil {
- err = os.NewSyscallError("CloseHandle", e)
- }
- close(w.internalEvent)
- close(w.Error)
- ch <- err
- return
- case in := <-w.input:
- switch in.op {
- case opAddWatch:
- in.reply <- w.addWatch(in.path, uint64(in.flags))
- case opRemoveWatch:
- in.reply <- w.remWatch(in.path)
- }
- default:
- }
- continue
- }
-
- switch e {
- case sys_ERROR_MORE_DATA:
- if watch == nil {
- w.Error <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")
- } else {
- // The i/o succeeded but the buffer is full.
- // In theory we should be building up a full packet.
- // In practice we can get away with just carrying on.
- n = uint32(unsafe.Sizeof(watch.buf))
- }
- case syscall.ERROR_ACCESS_DENIED:
- // Watched directory was probably removed
- w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF)
- w.deleteWatch(watch)
- w.startRead(watch)
- continue
- case syscall.ERROR_OPERATION_ABORTED:
- // CancelIo was called on this handle
- continue
- default:
- w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e)
- continue
- case nil:
- }
-
- var offset uint32
- for {
- if n == 0 {
- w.internalEvent <- &FileEvent{mask: sys_FS_Q_OVERFLOW}
- w.Error <- errors.New("short read in readEvents()")
- break
- }
-
- // Point "raw" to the event in the buffer
- raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
- buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
- name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
- fullname := watch.path + "\\" + name
-
- var mask uint64
- switch raw.Action {
- case syscall.FILE_ACTION_REMOVED:
- mask = sys_FS_DELETE_SELF
- case syscall.FILE_ACTION_MODIFIED:
- mask = sys_FS_MODIFY
- case syscall.FILE_ACTION_RENAMED_OLD_NAME:
- watch.rename = name
- case syscall.FILE_ACTION_RENAMED_NEW_NAME:
- if watch.names[watch.rename] != 0 {
- watch.names[name] |= watch.names[watch.rename]
- delete(watch.names, watch.rename)
- mask = sys_FS_MOVE_SELF
- }
- }
-
- sendNameEvent := func() {
- if w.sendEvent(fullname, watch.names[name]&mask) {
- if watch.names[name]&sys_FS_ONESHOT != 0 {
- delete(watch.names, name)
- }
- }
- }
- if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
- sendNameEvent()
- }
- if raw.Action == syscall.FILE_ACTION_REMOVED {
- w.sendEvent(fullname, watch.names[name]&sys_FS_IGNORED)
- delete(watch.names, name)
- }
- if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
- if watch.mask&sys_FS_ONESHOT != 0 {
- watch.mask = 0
- }
- }
- if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
- fullname = watch.path + "\\" + watch.rename
- sendNameEvent()
- }
-
- // Move to the next event in the buffer
- if raw.NextEntryOffset == 0 {
- break
- }
- offset += raw.NextEntryOffset
-
- // Error!
- if offset >= n {
- w.Error <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.")
- break
- }
- }
-
- if err := w.startRead(watch); err != nil {
- w.Error <- err
- }
- }
-}
-
-func (w *Watcher) sendEvent(name string, mask uint64) bool {
- if mask == 0 {
- return false
- }
- event := &FileEvent{mask: uint32(mask), Name: name}
- if mask&sys_FS_MOVE != 0 {
- if mask&sys_FS_MOVED_FROM != 0 {
- w.cookie++
- }
- event.cookie = w.cookie
- }
- select {
- case ch := <-w.quit:
- w.quit <- ch
- case w.Event <- event:
- }
- return true
-}
-
-func toWindowsFlags(mask uint64) uint32 {
- var m uint32
- if mask&sys_FS_ACCESS != 0 {
- m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
- }
- if mask&sys_FS_MODIFY != 0 {
- m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
- }
- if mask&sys_FS_ATTRIB != 0 {
- m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
- }
- if mask&(sys_FS_MOVE|sys_FS_CREATE|sys_FS_DELETE) != 0 {
- m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
- }
- return m
-}
-
-func toFSnotifyFlags(action uint32) uint64 {
- switch action {
- case syscall.FILE_ACTION_ADDED:
- return sys_FS_CREATE
- case syscall.FILE_ACTION_REMOVED:
- return sys_FS_DELETE
- case syscall.FILE_ACTION_MODIFIED:
- return sys_FS_MODIFY
- case syscall.FILE_ACTION_RENAMED_OLD_NAME:
- return sys_FS_MOVED_FROM
- case syscall.FILE_ACTION_RENAMED_NEW_NAME:
- return sys_FS_MOVED_TO
- }
- return 0
-}
diff --git a/Godeps/_workspace/src/github.com/mattn/go-runewidth/.travis.yml b/Godeps/_workspace/src/github.com/mattn/go-runewidth/.travis.yml
new file mode 100644
index 000000000..ad584f4da
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mattn/go-runewidth/.travis.yml
@@ -0,0 +1,9 @@
+language: go
+go:
+ - tip
+before_install:
+ - go get github.com/axw/gocov/gocov
+ - go get github.com/mattn/goveralls
+ - go get golang.org/x/tools/cmd/cover
+script:
+ - $HOME/gopath/bin/goveralls -repotoken lAKAWPzcGsD3A8yBX3BGGtRUdJ6CaGERL
diff --git a/Godeps/_workspace/src/github.com/mattn/go-runewidth/README.mkd b/Godeps/_workspace/src/github.com/mattn/go-runewidth/README.mkd
new file mode 100644
index 000000000..4f0d583be
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mattn/go-runewidth/README.mkd
@@ -0,0 +1,25 @@
+go-runewidth
+============
+
+[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth)
+[![Coverage Status](https://coveralls.io/repos/mattn/go-runewidth/badge.png?branch=HEAD)](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD)
+
+Provides functions to get fixed width of the character or string.
+
+Usage
+-----
+
+```go
+runewidth.StringWidth("つのだ☆HIRO") == 12
+```
+
+
+Author
+------
+
+Yasuhiro Matsumoto
+
+License
+-------
+
+under the MIT License: http://mattn.mit-license.org/2013
diff --git a/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth.go b/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth.go
new file mode 100644
index 000000000..0b417db15
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth.go
@@ -0,0 +1,404 @@
+package runewidth
+
+var EastAsianWidth = IsEastAsian()
+var DefaultCondition = &Condition{EastAsianWidth}
+
+type interval struct {
+ first rune
+ last rune
+}
+
+var combining = []interval{
+ {0x0300, 0x036F}, {0x0483, 0x0486}, {0x0488, 0x0489},
+ {0x0591, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2},
+ {0x05C4, 0x05C5}, {0x05C7, 0x05C7}, {0x0600, 0x0603},
+ {0x0610, 0x0615}, {0x064B, 0x065E}, {0x0670, 0x0670},
+ {0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED},
+ {0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A},
+ {0x07A6, 0x07B0}, {0x07EB, 0x07F3}, {0x0901, 0x0902},
+ {0x093C, 0x093C}, {0x0941, 0x0948}, {0x094D, 0x094D},
+ {0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0981},
+ {0x09BC, 0x09BC}, {0x09C1, 0x09C4}, {0x09CD, 0x09CD},
+ {0x09E2, 0x09E3}, {0x0A01, 0x0A02}, {0x0A3C, 0x0A3C},
+ {0x0A41, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D},
+ {0x0A70, 0x0A71}, {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC},
+ {0x0AC1, 0x0AC5}, {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD},
+ {0x0AE2, 0x0AE3}, {0x0B01, 0x0B01}, {0x0B3C, 0x0B3C},
+ {0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, {0x0B4D, 0x0B4D},
+ {0x0B56, 0x0B56}, {0x0B82, 0x0B82}, {0x0BC0, 0x0BC0},
+ {0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, {0x0C46, 0x0C48},
+ {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0CBC, 0x0CBC},
+ {0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD},
+ {0x0CE2, 0x0CE3}, {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D},
+ {0x0DCA, 0x0DCA}, {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6},
+ {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E},
+ {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC},
+ {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35},
+ {0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F71, 0x0F7E},
+ {0x0F80, 0x0F84}, {0x0F86, 0x0F87}, {0x0F90, 0x0F97},
+ {0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102D, 0x1030},
+ {0x1032, 0x1032}, {0x1036, 0x1037}, {0x1039, 0x1039},
+ {0x1058, 0x1059}, {0x1160, 0x11FF}, {0x135F, 0x135F},
+ {0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753},
+ {0x1772, 0x1773}, {0x17B4, 0x17B5}, {0x17B7, 0x17BD},
+ {0x17C6, 0x17C6}, {0x17C9, 0x17D3}, {0x17DD, 0x17DD},
+ {0x180B, 0x180D}, {0x18A9, 0x18A9}, {0x1920, 0x1922},
+ {0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193B},
+ {0x1A17, 0x1A18}, {0x1B00, 0x1B03}, {0x1B34, 0x1B34},
+ {0x1B36, 0x1B3A}, {0x1B3C, 0x1B3C}, {0x1B42, 0x1B42},
+ {0x1B6B, 0x1B73}, {0x1DC0, 0x1DCA}, {0x1DFE, 0x1DFF},
+ {0x200B, 0x200F}, {0x202A, 0x202E}, {0x2060, 0x2063},
+ {0x206A, 0x206F}, {0x20D0, 0x20EF}, {0x302A, 0x302F},
+ {0x3099, 0x309A}, {0xA806, 0xA806}, {0xA80B, 0xA80B},
+ {0xA825, 0xA826}, {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F},
+ {0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB},
+ {0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F},
+ {0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x1D167, 0x1D169},
+ {0x1D173, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD},
+ {0x1D242, 0x1D244}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F},
+ {0xE0100, 0xE01EF},
+}
+
+type ctype int
+
+const (
+ narrow ctype = iota
+ ambiguous
+ wide
+ halfwidth
+ fullwidth
+ neutral
+)
+
+type intervalType struct {
+ first rune
+ last rune
+ ctype ctype
+}
+
+var ctypes = []intervalType{
+ {0x0020, 0x007E, narrow},
+ {0x00A1, 0x00A1, ambiguous},
+ {0x00A2, 0x00A3, narrow},
+ {0x00A4, 0x00A4, ambiguous},
+ {0x00A5, 0x00A6, narrow},
+ {0x00A7, 0x00A8, ambiguous},
+ {0x00AA, 0x00AA, ambiguous},
+ {0x00AC, 0x00AC, narrow},
+ {0x00AD, 0x00AE, ambiguous},
+ {0x00AF, 0x00AF, narrow},
+ {0x00B0, 0x00B4, ambiguous},
+ {0x00B6, 0x00BA, ambiguous},
+ {0x00BC, 0x00BF, ambiguous},
+ {0x00C6, 0x00C6, ambiguous},
+ {0x00D0, 0x00D0, ambiguous},
+ {0x00D7, 0x00D8, ambiguous},
+ {0x00DE, 0x00E1, ambiguous},
+ {0x00E6, 0x00E6, ambiguous},
+ {0x00E8, 0x00EA, ambiguous},
+ {0x00EC, 0x00ED, ambiguous},
+ {0x00F0, 0x00F0, ambiguous},
+ {0x00F2, 0x00F3, ambiguous},
+ {0x00F7, 0x00FA, ambiguous},
+ {0x00FC, 0x00FC, ambiguous},
+ {0x00FE, 0x00FE, ambiguous},
+ {0x0101, 0x0101, ambiguous},
+ {0x0111, 0x0111, ambiguous},
+ {0x0113, 0x0113, ambiguous},
+ {0x011B, 0x011B, ambiguous},
+ {0x0126, 0x0127, ambiguous},
+ {0x012B, 0x012B, ambiguous},
+ {0x0131, 0x0133, ambiguous},
+ {0x0138, 0x0138, ambiguous},
+ {0x013F, 0x0142, ambiguous},
+ {0x0144, 0x0144, ambiguous},
+ {0x0148, 0x014B, ambiguous},
+ {0x014D, 0x014D, ambiguous},
+ {0x0152, 0x0153, ambiguous},
+ {0x0166, 0x0167, ambiguous},
+ {0x016B, 0x016B, ambiguous},
+ {0x01CE, 0x01CE, ambiguous},
+ {0x01D0, 0x01D0, ambiguous},
+ {0x01D2, 0x01D2, ambiguous},
+ {0x01D4, 0x01D4, ambiguous},
+ {0x01D6, 0x01D6, ambiguous},
+ {0x01D8, 0x01D8, ambiguous},
+ {0x01DA, 0x01DA, ambiguous},
+ {0x01DC, 0x01DC, ambiguous},
+ {0x0251, 0x0251, ambiguous},
+ {0x0261, 0x0261, ambiguous},
+ {0x02C4, 0x02C4, ambiguous},
+ {0x02C7, 0x02C7, ambiguous},
+ {0x02C9, 0x02CB, ambiguous},
+ {0x02CD, 0x02CD, ambiguous},
+ {0x02D0, 0x02D0, ambiguous},
+ {0x02D8, 0x02DB, ambiguous},
+ {0x02DD, 0x02DD, ambiguous},
+ {0x02DF, 0x02DF, ambiguous},
+ {0x0300, 0x036F, ambiguous},
+ {0x0391, 0x03A2, ambiguous},
+ {0x03A3, 0x03A9, ambiguous},
+ {0x03B1, 0x03C1, ambiguous},
+ {0x03C3, 0x03C9, ambiguous},
+ {0x0401, 0x0401, ambiguous},
+ {0x0410, 0x044F, ambiguous},
+ {0x0451, 0x0451, ambiguous},
+ {0x1100, 0x115F, wide},
+ {0x2010, 0x2010, ambiguous},
+ {0x2013, 0x2016, ambiguous},
+ {0x2018, 0x2019, ambiguous},
+ {0x201C, 0x201D, ambiguous},
+ {0x2020, 0x2022, ambiguous},
+ {0x2024, 0x2027, ambiguous},
+ {0x2030, 0x2030, ambiguous},
+ {0x2032, 0x2033, ambiguous},
+ {0x2035, 0x2035, ambiguous},
+ {0x203B, 0x203B, ambiguous},
+ {0x203E, 0x203E, ambiguous},
+ {0x2074, 0x2074, ambiguous},
+ {0x207F, 0x207F, ambiguous},
+ {0x2081, 0x2084, ambiguous},
+ {0x20A9, 0x20A9, halfwidth},
+ {0x20AC, 0x20AC, ambiguous},
+ {0x2103, 0x2103, ambiguous},
+ {0x2105, 0x2105, ambiguous},
+ {0x2109, 0x2109, ambiguous},
+ {0x2113, 0x2113, ambiguous},
+ {0x2116, 0x2116, ambiguous},
+ {0x2121, 0x2122, ambiguous},
+ {0x2126, 0x2126, ambiguous},
+ {0x212B, 0x212B, ambiguous},
+ {0x2153, 0x2154, ambiguous},
+ {0x215B, 0x215E, ambiguous},
+ {0x2160, 0x216B, ambiguous},
+ {0x2170, 0x2179, ambiguous},
+ {0x2189, 0x218A, ambiguous},
+ {0x2190, 0x2199, ambiguous},
+ {0x21B8, 0x21B9, ambiguous},
+ {0x21D2, 0x21D2, ambiguous},
+ {0x21D4, 0x21D4, ambiguous},
+ {0x21E7, 0x21E7, ambiguous},
+ {0x2200, 0x2200, ambiguous},
+ {0x2202, 0x2203, ambiguous},
+ {0x2207, 0x2208, ambiguous},
+ {0x220B, 0x220B, ambiguous},
+ {0x220F, 0x220F, ambiguous},
+ {0x2211, 0x2211, ambiguous},
+ {0x2215, 0x2215, ambiguous},
+ {0x221A, 0x221A, ambiguous},
+ {0x221D, 0x2220, ambiguous},
+ {0x2223, 0x2223, ambiguous},
+ {0x2225, 0x2225, ambiguous},
+ {0x2227, 0x222C, ambiguous},
+ {0x222E, 0x222E, ambiguous},
+ {0x2234, 0x2237, ambiguous},
+ {0x223C, 0x223D, ambiguous},
+ {0x2248, 0x2248, ambiguous},
+ {0x224C, 0x224C, ambiguous},
+ {0x2252, 0x2252, ambiguous},
+ {0x2260, 0x2261, ambiguous},
+ {0x2264, 0x2267, ambiguous},
+ {0x226A, 0x226B, ambiguous},
+ {0x226E, 0x226F, ambiguous},
+ {0x2282, 0x2283, ambiguous},
+ {0x2286, 0x2287, ambiguous},
+ {0x2295, 0x2295, ambiguous},
+ {0x2299, 0x2299, ambiguous},
+ {0x22A5, 0x22A5, ambiguous},
+ {0x22BF, 0x22BF, ambiguous},
+ {0x2312, 0x2312, ambiguous},
+ {0x2329, 0x232A, wide},
+ {0x2460, 0x24E9, ambiguous},
+ {0x24EB, 0x254B, ambiguous},
+ {0x2550, 0x2573, ambiguous},
+ {0x2580, 0x258F, ambiguous},
+ {0x2592, 0x2595, ambiguous},
+ {0x25A0, 0x25A1, ambiguous},
+ {0x25A3, 0x25A9, ambiguous},
+ {0x25B2, 0x25B3, ambiguous},
+ {0x25B6, 0x25B7, ambiguous},
+ {0x25BC, 0x25BD, ambiguous},
+ {0x25C0, 0x25C1, ambiguous},
+ {0x25C6, 0x25C8, ambiguous},
+ {0x25CB, 0x25CB, ambiguous},
+ {0x25CE, 0x25D1, ambiguous},
+ {0x25E2, 0x25E5, ambiguous},
+ {0x25EF, 0x25EF, ambiguous},
+ {0x2605, 0x2606, ambiguous},
+ {0x2609, 0x2609, ambiguous},
+ {0x260E, 0x260F, ambiguous},
+ {0x2614, 0x2615, ambiguous},
+ {0x261C, 0x261C, ambiguous},
+ {0x261E, 0x261E, ambiguous},
+ {0x2640, 0x2640, ambiguous},
+ {0x2642, 0x2642, ambiguous},
+ {0x2660, 0x2661, ambiguous},
+ {0x2663, 0x2665, ambiguous},
+ {0x2667, 0x266A, ambiguous},
+ {0x266C, 0x266D, ambiguous},
+ {0x266F, 0x266F, ambiguous},
+ {0x269E, 0x269F, ambiguous},
+ {0x26BE, 0x26BF, ambiguous},
+ {0x26C4, 0x26CD, ambiguous},
+ {0x26CF, 0x26E1, ambiguous},
+ {0x26E3, 0x26E3, ambiguous},
+ {0x26E8, 0x26FF, ambiguous},
+ {0x273D, 0x273D, ambiguous},
+ {0x2757, 0x2757, ambiguous},
+ {0x2776, 0x277F, ambiguous},
+ {0x27E6, 0x27ED, narrow},
+ {0x2985, 0x2986, narrow},
+ {0x2B55, 0x2B59, ambiguous},
+ {0x2E80, 0x2E9A, wide},
+ {0x2E9B, 0x2EF4, wide},
+ {0x2F00, 0x2FD6, wide},
+ {0x2FF0, 0x2FFC, wide},
+ {0x3000, 0x3000, fullwidth},
+ {0x3001, 0x303E, wide},
+ {0x3041, 0x3097, wide},
+ {0x3099, 0x3100, wide},
+ {0x3105, 0x312E, wide},
+ {0x3131, 0x318F, wide},
+ {0x3190, 0x31BB, wide},
+ {0x31C0, 0x31E4, wide},
+ {0x31F0, 0x321F, wide},
+ {0x3220, 0x3247, wide},
+ {0x3248, 0x324F, ambiguous},
+ {0x3250, 0x32FF, wide},
+ {0x3300, 0x4DBF, wide},
+ {0x4E00, 0xA48D, wide},
+ {0xA490, 0xA4C7, wide},
+ {0xA960, 0xA97D, wide},
+ {0xAC00, 0xD7A4, wide},
+ {0xE000, 0xF8FF, ambiguous},
+ {0xF900, 0xFAFF, wide},
+ {0xFE00, 0xFE0F, ambiguous},
+ {0xFE10, 0xFE1A, wide},
+ {0xFE30, 0xFE53, wide},
+ {0xFE54, 0xFE67, wide},
+ {0xFE68, 0xFE6C, wide},
+ {0xFF01, 0xFF60, fullwidth},
+ {0xFF61, 0xFFBF, halfwidth},
+ {0xFFC2, 0xFFC8, halfwidth},
+ {0xFFCA, 0xFFD0, halfwidth},
+ {0xFFD2, 0xFFD8, halfwidth},
+ {0xFFDA, 0xFFDD, halfwidth},
+ {0xFFE0, 0xFFE7, fullwidth},
+ {0xFFE8, 0xFFEF, halfwidth},
+ {0xFFFD, 0xFFFE, ambiguous},
+ {0x1B000, 0x1B002, wide},
+ {0x1F100, 0x1F10A, ambiguous},
+ {0x1F110, 0x1F12D, ambiguous},
+ {0x1F130, 0x1F169, ambiguous},
+ {0x1F170, 0x1F19B, ambiguous},
+ {0x1F200, 0x1F203, wide},
+ {0x1F210, 0x1F23B, wide},
+ {0x1F240, 0x1F249, wide},
+ {0x1F250, 0x1F252, wide},
+ {0x20000, 0x2FFFE, wide},
+ {0x30000, 0x3FFFE, wide},
+ {0xE0100, 0xE01F0, ambiguous},
+ {0xF0000, 0xFFFFD, ambiguous},
+ {0x100000, 0x10FFFE, ambiguous},
+}
+
+type Condition struct {
+ EastAsianWidth bool
+}
+
+func NewCondition() *Condition {
+ return &Condition{EastAsianWidth}
+}
+
+// RuneWidth returns the number of cells in r.
+// See http://www.unicode.org/reports/tr11/
+func (c *Condition) RuneWidth(r rune) int {
+ if r == 0 {
+ return 0
+ }
+ if r < 32 || (r >= 0x7f && r < 0xa0) {
+ return 1
+ }
+ for _, iv := range combining {
+ if iv.first <= r && r <= iv.last {
+ return 0
+ }
+ }
+
+ if c.EastAsianWidth && IsAmbiguousWidth(r) {
+ return 2
+ }
+
+ if r >= 0x1100 &&
+ (r <= 0x115f || r == 0x2329 || r == 0x232a ||
+ (r >= 0x2e80 && r <= 0xa4cf && r != 0x303f) ||
+ (r >= 0xac00 && r <= 0xd7a3) ||
+ (r >= 0xf900 && r <= 0xfaff) ||
+ (r >= 0xfe30 && r <= 0xfe6f) ||
+ (r >= 0xff00 && r <= 0xff60) ||
+ (r >= 0xffe0 && r <= 0xffe6) ||
+ (r >= 0x20000 && r <= 0x2fffd) ||
+ (r >= 0x30000 && r <= 0x3fffd)) {
+ return 2
+ }
+ return 1
+}
+
+func (c *Condition) StringWidth(s string) (width int) {
+ for _, r := range []rune(s) {
+ width += c.RuneWidth(r)
+ }
+ return width
+}
+
+func (c *Condition) Truncate(s string, w int, tail string) string {
+ r := []rune(s)
+ tw := StringWidth(tail)
+ w -= tw
+ width := 0
+ i := 0
+ for ; i < len(r); i++ {
+ cw := RuneWidth(r[i])
+ if width+cw > w {
+ break
+ }
+ width += cw
+ }
+ if i == len(r) {
+ return string(r[0:i])
+ }
+ return string(r[0:i]) + tail
+}
+
+// RuneWidth returns the number of cells in r.
+// See http://www.unicode.org/reports/tr11/
+func RuneWidth(r rune) int {
+ return DefaultCondition.RuneWidth(r)
+}
+
+func ct(r rune) ctype {
+ for _, iv := range ctypes {
+ if iv.first <= r && r <= iv.last {
+ return iv.ctype
+ }
+ }
+ return neutral
+}
+
+// IsAmbiguousWidth returns whether is ambiguous width or not.
+func IsAmbiguousWidth(r rune) bool {
+ return ct(r) == ambiguous
+}
+
+// IsAmbiguousWidth returns whether is ambiguous width or not.
+func IsNeutralWidth(r rune) bool {
+ return ct(r) == neutral
+}
+
+func StringWidth(s string) (width int) {
+ return DefaultCondition.StringWidth(s)
+}
+
+func Truncate(s string, w int, tail string) string {
+ return DefaultCondition.Truncate(s, w, tail)
+}
diff --git a/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_js.go b/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_js.go
new file mode 100644
index 000000000..0ce32c5e7
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_js.go
@@ -0,0 +1,8 @@
+// +build js
+
+package runewidth
+
+func IsEastAsian() bool {
+ // TODO: Implement this for the web. Detect east asian in a compatible way, and return true.
+ return false
+}
diff --git a/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_posix.go b/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_posix.go
new file mode 100644
index 000000000..a4495909d
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_posix.go
@@ -0,0 +1,69 @@
+// +build !windows,!js
+
+package runewidth
+
+import (
+ "os"
+ "regexp"
+ "strings"
+)
+
+var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`)
+
+func IsEastAsian() bool {
+ locale := os.Getenv("LC_CTYPE")
+ if locale == "" {
+ locale = os.Getenv("LANG")
+ }
+
+ // ignore C locale
+ if locale == "POSIX" || locale == "C" {
+ return false
+ }
+ if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') {
+ return false
+ }
+
+ charset := strings.ToLower(locale)
+ r := reLoc.FindStringSubmatch(locale)
+ if len(r) == 2 {
+ charset = strings.ToLower(r[1])
+ }
+
+ if strings.HasSuffix(charset, "@cjk_narrow") {
+ return false
+ }
+
+ for pos, b := range []byte(charset) {
+ if b == '@' {
+ charset = charset[:pos]
+ break
+ }
+ }
+
+ mbc_max := 1
+ switch charset {
+ case "utf-8", "utf8":
+ mbc_max = 6
+ case "jis":
+ mbc_max = 8
+ case "eucjp":
+ mbc_max = 3
+ case "euckr", "euccn":
+ mbc_max = 2
+ case "sjis", "cp932", "cp51932", "cp936", "cp949", "cp950":
+ mbc_max = 2
+ case "big5":
+ mbc_max = 2
+ case "gbk", "gb2312":
+ mbc_max = 2
+ }
+
+ if mbc_max > 1 && (charset[0] != 'u' ||
+ strings.HasPrefix(locale, "ja") ||
+ strings.HasPrefix(locale, "ko") ||
+ strings.HasPrefix(locale, "zh")) {
+ return true
+ }
+ return false
+}
diff --git a/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_test.go b/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_test.go
new file mode 100644
index 000000000..5cef3d6a4
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_test.go
@@ -0,0 +1,134 @@
+package runewidth
+
+import (
+ "testing"
+)
+
+var runewidthtests = []struct {
+ in rune
+ out int
+}{
+ {'世', 2},
+ {'界', 2},
+ {'セ', 1},
+ {'カ', 1},
+ {'イ', 1},
+ {'☆', 2}, // double width in ambiguous
+ {'\x00', 0},
+ {'\x01', 1},
+ {'\u0300', 0},
+}
+
+func TestRuneWidth(t *testing.T) {
+ c := NewCondition()
+ c.EastAsianWidth = true
+ for _, tt := range runewidthtests {
+ if out := c.RuneWidth(tt.in); out != tt.out {
+ t.Errorf("Width(%q) = %v, want %v", tt.in, out, tt.out)
+ }
+ }
+}
+
+var isambiguouswidthtests = []struct {
+ in rune
+ out bool
+}{
+ {'世', false},
+ {'■', true},
+ {'界', false},
+ {'○', true},
+ {'㈱', false},
+ {'①', true},
+ {'②', true},
+ {'③', true},
+ {'④', true},
+ {'⑤', true},
+ {'⑥', true},
+ {'⑦', true},
+ {'⑧', true},
+ {'⑨', true},
+ {'⑩', true},
+ {'⑪', true},
+ {'⑫', true},
+ {'⑬', true},
+ {'⑭', true},
+ {'⑮', true},
+ {'⑯', true},
+ {'⑰', true},
+ {'⑱', true},
+ {'⑲', true},
+ {'⑳', true},
+ {'☆', true},
+}
+
+func TestIsAmbiguousWidth(t *testing.T) {
+ for _, tt := range isambiguouswidthtests {
+ if out := IsAmbiguousWidth(tt.in); out != tt.out {
+ t.Errorf("IsAmbiguousWidth(%q) = %v, want %v", tt.in, out, tt.out)
+ }
+ }
+}
+
+var stringwidthtests = []struct {
+ in string
+ out int
+}{
+ {"■㈱の世界①", 12},
+ {"スター☆", 8},
+}
+
+func TestStringWidth(t *testing.T) {
+ c := NewCondition()
+ c.EastAsianWidth = true
+ for _, tt := range stringwidthtests {
+ if out := c.StringWidth(tt.in); out != tt.out {
+ t.Errorf("StringWidth(%q) = %v, want %v", tt.in, out, tt.out)
+ }
+ }
+}
+
+func TestStringWidthInvalid(t *testing.T) {
+ s := "こんにちわ\x00世界"
+ if out := StringWidth(s); out != 14 {
+ t.Errorf("StringWidth(%q) = %v, want %v", s, out, 14)
+ }
+}
+
+func TestTruncate(t *testing.T) {
+ s := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお"
+ expected := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおお..."
+
+ if out := Truncate(s, 80, "..."); out != expected {
+ t.Errorf("Truncate(%q) = %v, want %v", s, out, expected)
+ }
+}
+
+func TestTruncateNoNeeded(t *testing.T) {
+ s := "あいうえおあい"
+ expected := "あいうえおあい"
+
+ if out := Truncate(s, 80, "..."); out != expected {
+ t.Errorf("Truncate(%q) = %v, want %v", s, out, expected)
+ }
+}
+
+var isneutralwidthtests = []struct {
+ in rune
+ out bool
+}{
+ {'→', false},
+ {'┊', false},
+ {'┈', false},
+ {'~', false},
+ {'└', false},
+ {'⣀', true},
+ {'⣀', true},
+}
+
+func TestIsNeutralWidth(t *testing.T) {
+ for _, tt := range isneutralwidthtests {
+ if out := IsNeutralWidth(tt.in); out != tt.out {
+ t.Errorf("IsNeutralWidth(%q) = %v, want %v", tt.in, out, tt.out)
+ }
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_windows.go b/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_windows.go
new file mode 100644
index 000000000..bdd84454b
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mattn/go-runewidth/runewidth_windows.go
@@ -0,0 +1,24 @@
+package runewidth
+
+import (
+ "syscall"
+)
+
+var (
+ kernel32 = syscall.NewLazyDLL("kernel32")
+ procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP")
+)
+
+func IsEastAsian() bool {
+ r1, _, _ := procGetConsoleOutputCP.Call()
+ if r1 == 0 {
+ return false
+ }
+
+ switch int(r1) {
+ case 932, 51932, 936, 949, 950:
+ return true
+ }
+
+ return false
+}
diff --git a/Godeps/_workspace/src/github.com/nsf/termbox-go/AUTHORS b/Godeps/_workspace/src/github.com/nsf/termbox-go/AUTHORS
new file mode 100644
index 000000000..fe26fb0fb
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/nsf/termbox-go/AUTHORS
@@ -0,0 +1,4 @@
+# Please keep this file sorted.
+
+Georg Reinke <guelfey@googlemail.com>
+nsf <no.smile.face@gmail.com>
diff --git a/Godeps/_workspace/src/github.com/nsf/termbox-go/LICENSE b/Godeps/_workspace/src/github.com/nsf/termbox-go/LICENSE
new file mode 100644
index 000000000..d9bc068ce
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/nsf/termbox-go/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2012 termbox-go authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Godeps/_workspace/src/github.com/nsf/termbox-go/README.md b/Godeps/_workspace/src/github.com/nsf/termbox-go/README.md
new file mode 100644
index 000000000..334d75102
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/nsf/termbox-go/README.md
@@ -0,0 +1,21 @@
+## Termbox
+Termbox is a library that provides a minimalistic API which allows the programmer to write text-based user interfaces. The library is crossplatform and has both terminal-based implementations on *nix operating systems and a winapi console based implementation for windows operating systems. The basic idea is an abstraction of the greatest common subset of features available on all major terminals and other terminal-like APIs in a minimalistic fashion. Small API means it is easy to implement, test, maintain and learn it, that's what makes the termbox a distinct library in its area.
+
+### Installation
+Install and update this go package with `go get -u github.com/nsf/termbox-go`
+
+### Examples
+For examples of what can be done take a look at demos in the _demos directory. You can try them with go run: `go run _demos/keyboard.go`
+
+There are also some interesting projects using termbox-go:
+ - [godit](https://github.com/nsf/godit) is an emacsish lightweight text editor written using termbox.
+ - [gomatrix](https://github.com/GeertJohan/gomatrix) connects to The Matrix and displays its data streams in your terminal.
+ - [gotetris](https://github.com/jjinux/gotetris) is an implementation of Tetris.
+ - [sokoban-go](https://github.com/rn2dy/sokoban-go) is an implementation of sokoban game.
+ - [hecate](https://github.com/evanmiller/hecate) is a hex editor designed by Satan.
+ - [httopd](https://github.com/verdverm/httopd) is top for httpd logs.
+ - [mop](https://github.com/michaeldv/mop) is stock market tracker for hackers.
+ - [termui](https://github.com/gizak/termui) is a terminal dashboard.
+
+### API reference
+[godoc.org/github.com/nsf/termbox-go](http://godoc.org/github.com/nsf/termbox-go)
diff --git a/Godeps/_workspace/src/github.com/nsf/termbox-go/api.go b/Godeps/_workspace/src/github.com/nsf/termbox-go/api.go
new file mode 100644
index 000000000..b08bca61a
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/nsf/termbox-go/api.go
@@ -0,0 +1,451 @@
+// +build !windows
+
+package termbox
+
+import "github.com/mattn/go-runewidth"
+import "fmt"
+import "os"
+import "os/signal"
+import "syscall"
+import "runtime"
+
+// public API
+
+// Initializes termbox library. This function should be called before any other functions.
+// After successful initialization, the library must be finalized using 'Close' function.
+//
+// Example usage:
+// err := termbox.Init()
+// if err != nil {
+// panic(err)
+// }
+// defer termbox.Close()
+func Init() error {
+ var err error
+
+ out, err = os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
+ if err != nil {
+ return err
+ }
+ in, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
+ if err != nil {
+ return err
+ }
+
+ err = setup_term()
+ if err != nil {
+ return fmt.Errorf("termbox: error while reading terminfo data: %v", err)
+ }
+
+ signal.Notify(sigwinch, syscall.SIGWINCH)
+ signal.Notify(sigio, syscall.SIGIO)
+
+ _, err = fcntl(in, syscall.F_SETFL, syscall.O_ASYNC|syscall.O_NONBLOCK)
+ if err != nil {
+ return err
+ }
+ _, err = fcntl(in, syscall.F_SETOWN, syscall.Getpid())
+ if runtime.GOOS != "darwin" && err != nil {
+ return err
+ }
+ err = tcgetattr(out.Fd(), &orig_tios)
+ if err != nil {
+ return err
+ }
+
+ tios := orig_tios
+ tios.Iflag &^= syscall_IGNBRK | syscall_BRKINT | syscall_PARMRK |
+ syscall_ISTRIP | syscall_INLCR | syscall_IGNCR |
+ syscall_ICRNL | syscall_IXON
+ tios.Oflag &^= syscall_OPOST
+ tios.Lflag &^= syscall_ECHO | syscall_ECHONL | syscall_ICANON |
+ syscall_ISIG | syscall_IEXTEN
+ tios.Cflag &^= syscall_CSIZE | syscall_PARENB
+ tios.Cflag |= syscall_CS8
+ tios.Cc[syscall_VMIN] = 1
+ tios.Cc[syscall_VTIME] = 0
+
+ err = tcsetattr(out.Fd(), &tios)
+ if err != nil {
+ return err
+ }
+
+ out.WriteString(funcs[t_enter_ca])
+ out.WriteString(funcs[t_enter_keypad])
+ out.WriteString(funcs[t_hide_cursor])
+ out.WriteString(funcs[t_clear_screen])
+
+ termw, termh = get_term_size(out.Fd())
+ back_buffer.init(termw, termh)
+ front_buffer.init(termw, termh)
+ back_buffer.clear()
+ front_buffer.clear()
+
+ go func() {
+ buf := make([]byte, 128)
+ for {
+ select {
+ case <-sigio:
+ for {
+ n, err := syscall.Read(in, buf)
+ if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
+ break
+ }
+ select {
+ case input_comm <- input_event{buf[:n], err}:
+ ie := <-input_comm
+ buf = ie.data[:128]
+ case <-quit:
+ return
+ }
+ }
+ case <-quit:
+ return
+ }
+ }
+ }()
+
+ IsInit = true
+ return nil
+}
+
+// Interrupt an in-progress call to PollEvent by causing it to return
+// EventInterrupt. Note that this function will block until the PollEvent
+// function has successfully been interrupted.
+func Interrupt() {
+ interrupt_comm <- struct{}{}
+}
+
+// Finalizes termbox library, should be called after successful initialization
+// when termbox's functionality isn't required anymore.
+func Close() {
+ quit <- 1
+ out.WriteString(funcs[t_show_cursor])
+ out.WriteString(funcs[t_sgr0])
+ out.WriteString(funcs[t_clear_screen])
+ out.WriteString(funcs[t_exit_ca])
+ out.WriteString(funcs[t_exit_keypad])
+ out.WriteString(funcs[t_exit_mouse])
+ tcsetattr(out.Fd(), &orig_tios)
+
+ out.Close()
+ syscall.Close(in)
+
+ // reset the state, so that on next Init() it will work again
+ termw = 0
+ termh = 0
+ input_mode = InputEsc
+ out = nil
+ in = 0
+ lastfg = attr_invalid
+ lastbg = attr_invalid
+ lastx = coord_invalid
+ lasty = coord_invalid
+ cursor_x = cursor_hidden
+ cursor_y = cursor_hidden
+ foreground = ColorDefault
+ background = ColorDefault
+ IsInit = false
+}
+
+// Synchronizes the internal back buffer with the terminal.
+func Flush() error {
+ // invalidate cursor position
+ lastx = coord_invalid
+ lasty = coord_invalid
+
+ update_size_maybe()
+
+ for y := 0; y < front_buffer.height; y++ {
+ line_offset := y * front_buffer.width
+ for x := 0; x < front_buffer.width; {
+ cell_offset := line_offset + x
+ back := &back_buffer.cells[cell_offset]
+ front := &front_buffer.cells[cell_offset]
+ if back.Ch < ' ' {
+ back.Ch = ' '
+ }
+ w := runewidth.RuneWidth(back.Ch)
+ if w == 0 || w == 2 && runewidth.IsAmbiguousWidth(back.Ch) {
+ w = 1
+ }
+ if *back == *front {
+ x += w
+ continue
+ }
+ *front = *back
+ send_attr(back.Fg, back.Bg)
+
+ if w == 2 && x == front_buffer.width-1 {
+ // there's not enough space for 2-cells rune,
+ // let's just put a space in there
+ send_char(x, y, ' ')
+ } else {
+ send_char(x, y, back.Ch)
+ if w == 2 {
+ next := cell_offset + 1
+ front_buffer.cells[next] = Cell{
+ Ch: 0,
+ Fg: back.Fg,
+ Bg: back.Bg,
+ }
+ }
+ }
+ x += w
+ }
+ }
+ if !is_cursor_hidden(cursor_x, cursor_y) {
+ write_cursor(cursor_x, cursor_y)
+ }
+ return flush()
+}
+
+// Sets the position of the cursor. See also HideCursor().
+func SetCursor(x, y int) {
+ if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) {
+ outbuf.WriteString(funcs[t_show_cursor])
+ }
+
+ if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) {
+ outbuf.WriteString(funcs[t_hide_cursor])
+ }
+
+ cursor_x, cursor_y = x, y
+ if !is_cursor_hidden(cursor_x, cursor_y) {
+ write_cursor(cursor_x, cursor_y)
+ }
+}
+
+// The shortcut for SetCursor(-1, -1).
+func HideCursor() {
+ SetCursor(cursor_hidden, cursor_hidden)
+}
+
+// Changes cell's parameters in the internal back buffer at the specified
+// position.
+func SetCell(x, y int, ch rune, fg, bg Attribute) {
+ if x < 0 || x >= back_buffer.width {
+ return
+ }
+ if y < 0 || y >= back_buffer.height {
+ return
+ }
+
+ back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg}
+}
+
+// Returns a slice into the termbox's back buffer. You can get its dimensions
+// using 'Size' function. The slice remains valid as long as no 'Clear' or
+// 'Flush' function calls were made after call to this function.
+func CellBuffer() []Cell {
+ return back_buffer.cells
+}
+
+// After getting a raw event from PollRawEvent function call, you can parse it
+// again into an ordinary one using termbox logic. That is parse an event as
+// termbox would do it. Returned event in addition to usual Event struct fields
+// sets N field to the amount of bytes used within 'data' slice. If the length
+// of 'data' slice is zero or event cannot be parsed for some other reason, the
+// function will return a special event type: EventNone.
+//
+// IMPORTANT: EventNone may contain a non-zero N, which means you should skip
+// these bytes, because termbox cannot recognize them.
+//
+// NOTE: This API is experimental and may change in future.
+func ParseEvent(data []byte) Event {
+ event := Event{Type: EventKey}
+ ok := extract_event(data, &event)
+ if !ok {
+ return Event{Type: EventNone, N: event.N}
+ }
+ return event
+}
+
+// Wait for an event and return it. This is a blocking function call. Instead
+// of EventKey and EventMouse it returns EventRaw events. Raw event is written
+// into `data` slice and Event's N field is set to the amount of bytes written.
+// The minimum required length of the 'data' slice is 1. This requirement may
+// vary on different platforms.
+//
+// NOTE: This API is experimental and may change in future.
+func PollRawEvent(data []byte) Event {
+ if len(data) == 0 {
+ panic("len(data) >= 1 is a requirement")
+ }
+
+ var event Event
+ if extract_raw_event(data, &event) {
+ return event
+ }
+
+ for {
+ select {
+ case ev := <-input_comm:
+ if ev.err != nil {
+ return Event{Type: EventError, Err: ev.err}
+ }
+
+ inbuf = append(inbuf, ev.data...)
+ input_comm <- ev
+ if extract_raw_event(data, &event) {
+ return event
+ }
+ case <-interrupt_comm:
+ event.Type = EventInterrupt
+ return event
+
+ case <-sigwinch:
+ event.Type = EventResize
+ event.Width, event.Height = get_term_size(out.Fd())
+ return event
+ }
+ }
+}
+
+// Wait for an event and return it. This is a blocking function call.
+func PollEvent() Event {
+ var event Event
+
+ // try to extract event from input buffer, return on success
+ event.Type = EventKey
+ ok := extract_event(inbuf, &event)
+ if event.N != 0 {
+ copy(inbuf, inbuf[event.N:])
+ inbuf = inbuf[:len(inbuf)-event.N]
+ }
+ if ok {
+ return event
+ }
+
+ for {
+ select {
+ case ev := <-input_comm:
+ if ev.err != nil {
+ return Event{Type: EventError, Err: ev.err}
+ }
+
+ inbuf = append(inbuf, ev.data...)
+ input_comm <- ev
+ ok := extract_event(inbuf, &event)
+ if event.N != 0 {
+ copy(inbuf, inbuf[event.N:])
+ inbuf = inbuf[:len(inbuf)-event.N]
+ }
+ if ok {
+ return event
+ }
+ case <-interrupt_comm:
+ event.Type = EventInterrupt
+ return event
+
+ case <-sigwinch:
+ event.Type = EventResize
+ event.Width, event.Height = get_term_size(out.Fd())
+ return event
+ }
+ }
+ panic("unreachable")
+}
+
+// Returns the size of the internal back buffer (which is mostly the same as
+// terminal's window size in characters). But it doesn't always match the size
+// of the terminal window, after the terminal size has changed, the internal
+// back buffer will get in sync only after Clear or Flush function calls.
+func Size() (int, int) {
+ return termw, termh
+}
+
+// Clears the internal back buffer.
+func Clear(fg, bg Attribute) error {
+ foreground, background = fg, bg
+ err := update_size_maybe()
+ back_buffer.clear()
+ return err
+}
+
+// Sets termbox input mode. Termbox has two input modes:
+//
+// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match
+// any known sequence. ESC means KeyEsc. This is the default input mode.
+//
+// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match
+// any known sequence. ESC enables ModAlt modifier for the next keyboard event.
+//
+// Both input modes can be OR'ed with Mouse mode. Setting Mouse mode bit up will
+// enable mouse button click events.
+//
+// If 'mode' is InputCurrent, returns the current input mode. See also Input*
+// constants.
+func SetInputMode(mode InputMode) InputMode {
+ if mode == InputCurrent {
+ return input_mode
+ }
+ if mode&InputMouse != 0 {
+ out.WriteString(funcs[t_enter_mouse])
+ } else {
+ out.WriteString(funcs[t_exit_mouse])
+ }
+
+ input_mode = mode
+ return input_mode
+}
+
+// Sets the termbox output mode. Termbox has four output options:
+// 1. OutputNormal => [1..8]
+// This mode provides 8 different colors:
+// black, red, green, yellow, blue, magenta, cyan, white
+// Shortcut: ColorBlack, ColorRed, ...
+// Attributes: AttrBold, AttrUnderline, AttrReverse
+//
+// Example usage:
+// SetCell(x, y, '@', ColorBlack | AttrBold, ColorRed);
+//
+// 2. Output256 => [1..256]
+// In this mode you can leverage the 256 terminal mode:
+// 0x00 - 0x07: the 8 colors as in OutputNormal
+// 0x08 - 0x0f: Color* | AttrBold
+// 0x10 - 0xe7: 216 different colors
+// 0xe8 - 0xff: 24 different shades of grey
+//
+// Example usage:
+// SetCell(x, y, '@', 184, 240);
+// SetCell(x, y, '@', 0xb8, 0xf0);
+//
+// 3. Output216 => [1..216]
+// This mode supports the 3rd range of the 256 mode only.
+// But you dont need to provide an offset.
+//
+// 4. OutputGrayscale => [1..24]
+// This mode supports the 4th range of the 256 mode only.
+// But you dont need to provide an offset.
+//
+// In all modes, 0 represents the default color.
+//
+// `go run _demos/output.go` to see its impact on your terminal.
+//
+// If 'mode' is OutputCurrent, it returns the current output mode.
+//
+// Note that this may return a different OutputMode than the one requested,
+// as the requested mode may not be available on the target platform.
+func SetOutputMode(mode OutputMode) OutputMode {
+ if mode == OutputCurrent {
+ return output_mode
+ }
+
+ output_mode = mode
+ return output_mode
+}
+
+// Sync comes handy when something causes desync between termbox's understanding
+// of a terminal buffer and the reality. Such as a third party process. Sync
+// forces a complete resync between the termbox and a terminal, it may not be
+// visually pretty though.
+func Sync() error {
+ front_buffer.clear()
+ err := send_clear()
+ if err != nil {
+ return err
+ }
+
+ return Flush()
+}
diff --git a/Godeps/_workspace/src/github.com/nsf/termbox-go/api_common.go b/Godeps/_workspace/src/github.com/nsf/termbox-go/api_common.go
new file mode 100644
index 000000000..c0069fb28
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/nsf/termbox-go/api_common.go