--- /dev/null
+/* (The MIT License)
+ *
+ * Copyright (c) 2014 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ *
+ * 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.
+ */
+
+/*
+ * Link with libusb >= 1.0.0 (which uses the newer style api)
+ *
+ * gcc -Wall button.c -lusb-1.0.0 -I /usr/include/libusb-1.0/
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include <unistd.h>
+#include <signal.h>
+
+#include <libusb.h>
+
+static bool debug = false;
+
+#define DEBUG(_fmt, ...) if (debug) fprintf(stdout, "brb: " _fmt "\n", ## __VA_ARGS__)
+#define INFO(_fmt, ...) fprintf(stdout, "brb: "_fmt "\n", ## __VA_ARGS__)
+#define ERROR(_fmt, ...) fprintf(stderr, "brb: " _fmt "\n", ## __VA_ARGS__)
+
+typedef enum button_state {
+ BUTTON_STATE_ERROR = -1, //!< Error occurred getting button state
+ BUTTON_STATE_UNKNOWN = 0,
+ BUTTON_STATE_LID_CLOSED = 0x15, //!< Button lid is closed
+ BUTTON_STATE_PRESSED = 0x16, //!< Button is currently depressed (and lid is open)
+ BUTTON_STATE_LID_OPEN = 0x17 //!< Button lid is open
+} button_states_t;
+
+#define BRB_VID 0x1d34 //!< Big Red Button Vendor ID
+#define BRB_PID 0x0008 //!< Big Red Button Product ID
+#define BRB_POLL_INTERVAL 20000 //!< How long we wait in between polling the button
+
+static char const *progname; //!< What the binary's called
+static bool should_exit = false; //!< If true, break out of the poll loop.
+
+static bool kernel_was_attached;
+
+/** Find the device, and detach the kernel driver
+ *
+ */
+static struct libusb_device_handle *get_button_handle(void){
+ struct libusb_device_handle *handle = NULL;
+ int ret;
+
+ DEBUG("Attempting to open device (vendor 0x%04x, device 0x%04x)", BRB_VID, BRB_PID);
+ handle = libusb_open_device_with_vid_pid(NULL, BRB_VID, BRB_PID);
+ if (!handle) {
+ ERROR("Failed opening device descriptor (you may need to be root)...");
+ return NULL;
+ }
+
+ /* If the kernel driver is active, we need to detach it */
+ if (libusb_kernel_driver_active(handle, 0)) {
+ DEBUG("Kernel driver active, attempting to detach...");
+ ret = libusb_detach_kernel_driver(handle, 0);
+ if (ret < 0 ){
+ ERROR("Can't detach kernel driver");
+ return NULL;
+ }
+
+ kernel_was_attached = true;
+ }
+
+ return handle;
+}
+
+
+static int set_button_control(struct libusb_device_handle *handle)
+{
+ int ret;
+ uint8_t state[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 };
+
+ ret = libusb_control_transfer(handle, 0x21, 0x09, 0x00, 0x00, state, 8, 0);
+ if (ret < 0) {
+ ERROR("Error reading response %i", ret);
+ return -1;
+ }
+ if (ret == 0) {
+ ERROR("Device didn't send enough data");
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Returns the current button state
+ *
+ */
+static button_states_t read_button_state(struct libusb_device_handle *handle) {
+
+ uint8_t data[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ int dev[8];
+ int ret;
+
+ ret = set_button_control(handle);
+ if (ret < 0) {
+ return -1;
+ }
+
+ /* Send 0x81 to the EP to retrieve the state */
+ ret = libusb_interrupt_transfer(handle, 0x81, data, 8, dev, 200);
+ if (ret < 0) {
+ ERROR("Error getting interrupt data");
+ return -1;
+ }
+
+// printf("0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+// data[0],
+// data[1],
+// data[2],
+// data[3],
+// data[4],
+// data[5],
+// data[6],
+// data[7]
+// );
+
+ return (data[0] == 0x1A ? BUTTON_STATE_PRESSED : BUTTON_STATE_UNKNOWN);
+}
+
+void run_command(char const *cmd) {
+ int ret;
+
+ DEBUG("Running command: %s", cmd);
+ ret = system(cmd);
+ DEBUG("Command returned %i", ret);
+}
+
+/** Cleanup gracefully
+ *
+ */
+void exit_handler(int sig_num); /* Prototype for signal */
+void exit_handler(int sig_num)
+{
+ signal(SIGINT, exit_handler);
+ should_exit = true;
+}
+
+static void usage(int status)
+{
+ FILE *output = status ? stderr : stdout;
+
+ fprintf(output, "Usage: %s [options]\n", progname);
+ fprintf(output, " -P <microsends> Polling interval\n");
+ fprintf(output, " -o <command> Command to execute when button lid is open.\n");
+ fprintf(output, " -c <command> Command to execute when button lid is closed.\n");
+ fprintf(output, " -p <command> Command to execute when button is pressed.\n");
+ fprintf(output, " -r <command> Command to execute when button is released.\n");
+ fprintf(output, " -h This help text.\n");
+ fprintf(output, " -v Turn on verbose output.\n");
+
+ exit(status);
+}
+
+/** Main program
+ *
+ */
+int main (int argc, char *argv[]) {
+ char c;
+
+ char const *cmd_lid_open = NULL;
+ char const *cmd_lid_closed = NULL;
+ char const *cmd_pressed = NULL;
+ char const *cmd_released = NULL;
+
+ int interval = BRB_POLL_INTERVAL;
+
+ struct libusb_device_handle *handle = NULL;
+ button_states_t then = BUTTON_STATE_UNKNOWN, now;
+
+ progname = argv[0];
+
+ while ((c = getopt(argc, argv, "P:o:c:p:r:hv")) != EOF) {
+ switch (c) {
+ case 'P':
+ interval = atoi(optarg);
+ break;
+
+ case 'o':
+ cmd_lid_open = optarg;
+ break;
+
+ case 'c':
+ cmd_lid_closed = optarg;
+ break;
+
+ case 'p':
+ cmd_released = optarg;
+ break;
+
+ case 'r':
+ cmd_pressed = optarg;
+ break;
+
+ case 'h':
+ usage(0);
+ break;
+
+ case 'v':
+ debug = true;
+ break;
+ }
+ }
+
+ /* Setup a signal handler, so we can cleanup gracefully */
+ signal(SIGINT, exit_handler);
+
+ /* Initialise libusb (with the default context) */
+ libusb_init(NULL);
+
+#if defined(LIBUSB_LOG_LEVEL_DEBUG) && defined(LIBUSB_LOG_LEVEL_ERROR)
+ /* All the debugging messages !*/
+ if (debug) {
+ libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_DEBUG);
+ /* We still want to know about errors (helps with debugging) */
+ } else {
+ libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_ERROR);
+ }
+#endif
+
+ /* If we can't get the handle, exit... */
+ handle = get_button_handle();
+ if (!handle) exit(1);
+
+ if (read_button_state(handle) == BUTTON_STATE_ERROR) goto finish;
+
+ /* Loop, polling the device to get it's status */
+ while (should_exit != true) {
+ now = read_button_state(handle);
+ if (now == BUTTON_STATE_ERROR) goto skip;
+ if (then == now) goto skip;
+
+ if (then == BUTTON_STATE_PRESSED) {
+ /* Run the released action */
+ INFO("RELEASED");
+ if (cmd_released) run_command(cmd_released);
+
+ /* Weird but I guess it could happen... */
+ if (now == BUTTON_STATE_LID_CLOSED) goto closed;
+ goto next;
+ }
+
+ switch (now) {
+ case BUTTON_STATE_PRESSED:
+ /* Run the pressed action */
+ INFO("PRESSED");
+ if (cmd_pressed) run_command(cmd_pressed);
+ break;
+
+ case BUTTON_STATE_LID_OPEN:
+ /* Run the lid open action */
+ INFO("LID_OPEN");
+ if (cmd_lid_open) run_command(cmd_lid_open);
+ break;
+
+ case BUTTON_STATE_LID_CLOSED:
+ closed:
+ /* Run the closed action */
+ INFO("LID_CLOSED");
+ if (cmd_lid_closed) run_command(cmd_lid_closed);
+ break;
+
+ default:
+ goto skip;
+ }
+
+ next:
+ then = now;
+ skip:
+ usleep(interval);
+ }
+
+finish:
+ DEBUG("Exiting...");
+
+ fflush(stdout);
+ fflush(stderr);
+
+// if (kernel_was_attached) {
+// libusb_attach_kernel_handle(ghandle, 0);
+// }
+
+ libusb_close(handle);
+
+ exit(0);
+}
--- /dev/null
+diff --git a/TODO.md b/TODO.md
+index fcfe8a5..475e3bb 100644
+--- a/TODO.md
++++ b/TODO.md
+@@ -37,3 +37,14 @@ Maybe think about addressing these later:
+ * Find shortcut should select previous word if current char is newline
+ * implement command diffing logic to optimize the undo/redo log
+ * right click negative numbers should jump to that many lines from the end of file
++
++
++1P Move the cursor
++1P 1R 1P Select clicked on word
++1P 1R 1P 1R 1P Select whole non whitespace thing
++2P 2R Execute clicked word
++2P 1P 2R Execute clicked word with argument
++3P 3R Fetch clicked word
++3P 1P 3R ?? Go to definition ??
++1P 2P 2R Cut the selected text
++1P 3P 3R Paste the selection
+diff --git a/inc/edit.h b/inc/edit.h
+index b9e2881..01fdcee 100644
+--- a/inc/edit.h
++++ b/inc/edit.h
+@@ -127,7 +127,8 @@ Row* view_getrow(View* view, size_t row);
+ void view_byrune(View* view, int move, bool extsel);
+ void view_byword(View* view, int move, bool extsel);
+ void view_byline(View* view, int move, bool extsel);
+-char* view_fetch(View* view, size_t row, size_t col, bool (*isword)(Rune));
++size_t view_getoffset(View* view, size_t row, size_t col);
++char* view_fetch(View* view, size_t off, bool (*isword)(Rune));
+ bool view_findstr(View* view, int dir, char* str);
+ void view_insert(View* view, Rune rune);
+ void view_delete(View* view, int dir, bool byword);
+@@ -149,10 +150,10 @@ void view_scrollpage(View* view, int move);
+ void view_setln(View* view, size_t line);
+ size_t view_selsize(View* view);
+ void view_selprev(View* view);
+-void view_setcursor(View* view, size_t row, size_t col, bool extsel);
++void view_setcursor(View* view, size_t off, bool extsel);
+ void view_selextend(View* view, size_t row, size_t col);
+-void view_selword(View* view, size_t row, size_t col);
+-void view_select(View* view, size_t row, size_t col);
++void view_selword(View* view, size_t off);
++void view_select(View* view, size_t off);
+ void view_jumpto(View* view, bool extsel, size_t off);
+ void view_scrollto(View* view, size_t csr);
+ Rune view_getrune(View* view);
+diff --git a/src/lib/view.c b/src/lib/view.c
+index 40d2865..e6f6a62 100644
+--- a/src/lib/view.c
++++ b/src/lib/view.c
+@@ -204,7 +204,7 @@ void view_byline(View* view, int move, bool extsel) {
+ move_selection(view, extsel, move, buf_byline);
+ }
+
+-static size_t getoffset(View* view, size_t row, size_t col) {
++size_t view_getoffset(View* view, size_t row, size_t col) {
+ size_t i = 0, y = 0, idx = view->index + row;
+ if (idx >= view->nrows) return 0;
+ Row* selrow = view->rows[idx];
+@@ -215,16 +215,15 @@ static size_t getoffset(View* view, size_t row, size_t col) {
+ return selrow->cols[i].off;
+ }
+
+-void view_setcursor(View* view, size_t row, size_t col, bool extsel) {
+- getsel(view)->end = getoffset(view, row, col);
++void view_setcursor(View* view, size_t off, bool extsel) {
++ getsel(view)->end = off;
+ if (!extsel)
+ getsel(view)->beg = getsel(view)->end;
+ buf_getcol(BUF);
+ }
+
+-void view_selword(View* view, size_t row, size_t col) {
+- if (row != SIZE_MAX && col != SIZE_MAX)
+- view_setcursor(view, row, col, false);
++void view_selword(View* view, size_t off) {
++ view_setcursor(view, off, false);
+ buf_selword(BUF, risbigword);
+ }
+
+@@ -235,8 +234,8 @@ void view_selprev(View* view) {
+ buf_selclr(BUF, RIGHT);
+ }
+
+-void view_select(View* view, size_t row, size_t col) {
+- view_setcursor(view, row, col, false);
++void view_select(View* view, size_t off) {
++ view_setcursor(view, off, false);
+ buf_selctx(BUF, risword);
+ }
+
+@@ -244,12 +243,8 @@ size_t view_selsize(View* view) {
+ return buf_selsz(BUF);
+ }
+
+-char* view_fetch(View* view, size_t row, size_t col, bool (*isword)(Rune)) {
+- char* str = NULL;
+- size_t off = getoffset(view, row, col);
+- if (off != SIZE_MAX)
+- str = buf_fetch(BUF, isword, off);
+- return str;
++char* view_fetch(View* view, size_t off, bool (*isword)(Rune)) {
++ return buf_fetch(BUF, isword, off);
+ }
+
+ bool view_findstr(View* view, int dir, char* str) {
+diff --git a/src/pick.c b/src/pick.c
+index 9c525cd..503b49d 100644
+--- a/src/pick.c
++++ b/src/pick.c
+@@ -59,7 +59,7 @@ static int by_score(const void* a, const void* b) {
+ else if (ca->score > cb->score)
+ return -1;
+ else
+- return 0;
++ return strcmp(ca->string, cb->string);
+ }
+
+ static void load_choices(XConf* x) {
+diff --git a/src/tide.c b/src/tide.c
+index 7a05bc3..28ad559 100644
+--- a/src/tide.c
++++ b/src/tide.c
+@@ -115,7 +115,7 @@ static uint32_t getkey(XConf* x, XEvent* e) {
+ return special_keys(key);
+ }
+
+-static void mouse_left(WinRegion id, bool pressed, size_t row, size_t col) {
++static void mouse_left(WinRegion id, bool pressed, size_t off) {
+ static int count = 0;
+ static Time before = 0;
+ if (!pressed) return;
+@@ -128,42 +128,42 @@ static void mouse_left(WinRegion id, bool pressed, size_t row, size_t col) {
+ char* arg = NULL;
+ if (!arg || !*arg) arg = view_getstr(win_view(EDIT));
+ if (!arg || !*arg) arg = view_getstr(win_view(TAGS));
+- char* str = view_fetch(win_view(id), row, col, riscmd);
++ char* str = view_fetch(win_view(id), off, riscmd);
+ if (str) exec(str, arg);
+ free(str);
+ free(arg);
+ ExecRequest = 0;
+ } else {
+ if (count == 1)
+- view_setcursor(win_view(id), row, col, win_keymodsset(ModShift));
++ view_setcursor(win_view(id), off, win_keymodsset(ModShift));
+ else if (count == 2)
+- view_select(win_view(id), row, col);
++ view_select(win_view(id), off);
+ else if (count == 3)
+- view_selword(win_view(id), row, col);
++ view_selword(win_view(id), off);
+ }
+ }
+
+-static void mouse_middle(WinRegion id, bool pressed, size_t row, size_t col) {
++static void mouse_middle(WinRegion id, bool pressed, size_t off) {
+ if (pressed) { ExecRequest = 1; return; }
+ if (PRESSED(MouseLeft)) {
+ cut(NULL);
+ } else if (ExecRequest) {
+- char* str = view_fetch(win_view(id), row, col, riscmd);
++ char* str = view_fetch(win_view(id), off, riscmd);
+ if (str) exec(str, NULL);
+ free(str);
+ }
+ }
+
+-static void mouse_right(WinRegion id, bool pressed, size_t row, size_t col) {
++static void mouse_right(WinRegion id, bool pressed, size_t off) {
+ if (pressed) return;
+ if (PRESSED(MouseLeft)) {
+ paste(NULL);
+ } else {
+- FetchCmd[2] = view_fetch(win_view(id), row, col, risfile);
++ FetchCmd[2] = view_fetch(win_view(id), off, risfile);
+ if (job_run(FetchCmd) != 0) {
+ SearchDir *= (win_keymodsset(ModShift) ? -1 : +1);
+ free(SearchTerm);
+- SearchTerm = view_fetch(win_view(id), row, col, risfile);
++ SearchTerm = view_fetch(win_view(id), off, risfile);
+ view_findstr(win_view(EDIT), SearchDir, SearchTerm);
+ SyncMouse = true;
+ }
+@@ -180,10 +180,12 @@ static void mouse_click(int btn, bool pressed, int x, int y) {
+ size_t row, col;
+ Focused = (y <= Divider ? TAGS : EDIT);
+ get_position(Focused, x, y, &row, &col);
++ size_t off = view_getoffset(win_view(Focused), row, col);
++// printf("B%d %d %lu\n", btn, pressed, off);
+ switch(btn) {
+- case MouseLeft: mouse_left(Focused, pressed, row, col); break;
+- case MouseMiddle: mouse_middle(Focused, pressed, row, col); break;
+- case MouseRight: mouse_right(Focused, pressed, row, col); break;
++ case MouseLeft: mouse_left(Focused, pressed, off); break;
++ case MouseMiddle: mouse_middle(Focused, pressed, off); break;
++ case MouseRight: mouse_right(Focused, pressed, off); break;
+ case MouseWheelUp: mouse_scroll(Focused, pressed, -ScrollBy); break;
+ case MouseWheelDn: mouse_scroll(Focused, pressed, +ScrollBy); break;
+ }
+@@ -225,18 +227,46 @@ static void xkeypress(XConf* x, XEvent* e) {
+ view_insert(win_view(FOCUSED), key);
+ }
+
++static void xbtnhandle(XConf* x, XEvent* e) {
++ (void)x;
++ bool press = (e->type == ButtonPress);
++ switch (e->xbutton.button) {
++ case Button1:
++ printf("left\n");
++ break;
++
++ case Button2:
++ if (e->xbutton.state & (1 << (Button1 + 7)))
++ printf("left+middle\n");
++ else
++ printf("middle\n");
++ break;
++
++ case Button3:
++ if (e->xbutton.state & (1 << (Button1 + 7)))
++ printf("left+right\n");
++ else
++ printf("right\n");
++ break;
++ }
++ (void)press;
++}
++
+ static void xbtnpress(XConf* x, XEvent* e) {
+ (void)x;
+ Now = e->xbutton.time;
+ KeyBtnState = (e->xbutton.state | (1 << (e->xbutton.button + 7)));
+- mouse_click(e->xbutton.button, true, e->xbutton.x, e->xbutton.y);
++(void)mouse_click;
++// mouse_click(e->xbutton.button, true, e->xbutton.x, e->xbutton.y);
++ xbtnhandle(x,e);
+ }
+
+ static void xbtnrelease(XConf* x, XEvent* e) {
+ (void)x;
+ Now = e->xbutton.time;
+ KeyBtnState = (KeyBtnState & ~(1 << (e->xbutton.button + 7)));
+- mouse_click(e->xbutton.button, false, e->xbutton.x, e->xbutton.y);
++// mouse_click(e->xbutton.button, false, e->xbutton.x, e->xbutton.y);
++ xbtnhandle(x,e);
+ }
+
+ static void xbtnmotion(XConf* x, XEvent* e) {
+@@ -246,8 +276,9 @@ static void xbtnmotion(XConf* x, XEvent* e) {
+ KeyBtnState = e->xbutton.state;
+ int xpos = e->xbutton.x, ypos = e->xbutton.y;
+ get_position(Focused, xpos, ypos, &row, &col);
++ size_t off = view_getoffset(win_view(Focused), row, col);
+ if (PRESSED(MouseLeft))
+- view_setcursor(win_view(Focused), row, col, true);
++ view_setcursor(win_view(Focused), off, true);
+ }
+
+ static void xclientmsg(XConf* x, XEvent* e) {
+@@ -947,6 +978,16 @@ int main(int argc, char** argv) {
+ if (!gethostname(host, sizeof(host)))
+ win_prop_set("HOST", "host", host);
+
++ /* check if we should embede in a parent */
++ char* winid = getenv("TIDE_PARENT");
++ if (winid)
++ {
++ Window parent = strtoul(winid, 0, 0);
++ XReparentWindow(X.display, X.self, parent, 0, 0);
++ XFlush(X.display);
++ }
++
++
+ /* now create the window and start the event loop */
+ xupdate(NULL);
+ win_loop();