diff --git a/src/terminal/scrollbar.c b/src/terminal/scrollbar.c index c1a5bc9a..e0643976 100644 --- a/src/terminal/scrollbar.c +++ b/src/terminal/scrollbar.c @@ -67,6 +67,9 @@ guac_terminal_scrollbar* guac_terminal_scrollbar_alloc(guac_client* client, scrollbar->container = guac_client_alloc_layer(client); scrollbar->handle = guac_client_alloc_layer(client); + /* Init mouse event state tracking */ + scrollbar->dragging_handle = 0; + /* Reposition and resize to fit parent */ guac_terminal_scrollbar_parent_resized(scrollbar, parent_width, parent_height, visible_area); @@ -141,15 +144,36 @@ static void calculate_render_state(guac_terminal_scrollbar* scrollbar, if (render_state->handle_height > max_handle_height) render_state->handle_height = max_handle_height; - /* Calculate handle position */ + /* Calculate handle X position */ render_state->handle_x = GUAC_TERMINAL_SCROLLBAR_PADDING; + /* Calculate handle Y range */ + int min_handle_y = GUAC_TERMINAL_SCROLLBAR_PADDING; + int max_handle_y = min_handle_y + max_handle_height + - render_state->handle_height; + + /* Position handle relative to mouse if being dragged */ + if (scrollbar->dragging_handle) { + + int dragged_handle_y = scrollbar->drag_current_y + - scrollbar->drag_offset_y; + + /* Keep handle within bounds */ + if (dragged_handle_y < min_handle_y) + dragged_handle_y = min_handle_y; + else if (dragged_handle_y > max_handle_y) + dragged_handle_y = max_handle_y; + + render_state->handle_y = dragged_handle_y; + + } + /* Handle Y position is relative to current scroll value */ - if (scroll_delta > 0) - render_state->handle_y = GUAC_TERMINAL_SCROLLBAR_PADDING - + (max_handle_height - render_state->handle_height) - * (scrollbar->value - scrollbar->min) - / scroll_delta; + else if (scroll_delta > 0) + render_state->handle_y = min_handle_y + + (max_handle_y - min_handle_y) + * (scrollbar->value - scrollbar->min) + / scroll_delta; /* ... unless there is only one possible scroll value */ else @@ -278,3 +302,55 @@ void guac_terminal_scrollbar_parent_resized(guac_terminal_scrollbar* scrollbar, } +int guac_terminal_scrollbar_handle_mouse(guac_terminal_scrollbar* scrollbar, + int x, int y, int mask) { + + /* Get container rectangle bounds */ + int parent_left = scrollbar->render_state.container_x; + int parent_top = scrollbar->render_state.container_y; + int parent_right = parent_left + scrollbar->render_state.container_width; + int parent_bottom = parent_top + scrollbar->render_state.container_height; + + /* Calculate handle rectangle bounds */ + int handle_left = parent_left + scrollbar->render_state.handle_x; + int handle_top = parent_top + scrollbar->render_state.handle_y; + int handle_right = handle_left + scrollbar->render_state.handle_width; + int handle_bottom = handle_top + scrollbar->render_state.handle_height; + + /* Handle click on handle */ + if (scrollbar->dragging_handle) { + + /* Update drag while mouse button is held */ + if (mask & GUAC_CLIENT_MOUSE_LEFT) { + scrollbar->drag_current_y = y; + /* TODO: Update scrollbar value */ + } + + /* Stop drag if mouse button is released */ + else + scrollbar->dragging_handle = 0; + + /* Mouse event was handled by scrollbar */ + return 1; + + } + else if (mask == GUAC_CLIENT_MOUSE_LEFT + && x >= handle_left && x < handle_right + && y >= handle_top && y < handle_bottom) { + + /* Start drag */ + scrollbar->dragging_handle = 1; + scrollbar->drag_offset_y = y - handle_top; + scrollbar->drag_current_y = y; + + /* Mouse event was handled by scrollbar */ + return 1; + + } + + /* Eat any events that occur within the scrollbar */ + return x >= parent_left && x < parent_right + && y >= parent_top && y < parent_bottom; + +} + diff --git a/src/terminal/scrollbar.h b/src/terminal/scrollbar.h index e1bee7ff..f46a42a8 100644 --- a/src/terminal/scrollbar.h +++ b/src/terminal/scrollbar.h @@ -159,6 +159,24 @@ typedef struct guac_terminal_scrollbar { */ guac_terminal_scrollbar_render_state render_state; + /** + * Whether the scrollbar handle is currently being dragged. + */ + int dragging_handle; + + /** + * The offset of the Y location of the mouse pointer when the dragging + * began, relative to the top of the scrollbar handle. If dragging is not + * in progress, this value is undefined. + */ + int drag_offset_y; + + /** + * The current Y location of the mouse pointer if dragging is in progress. + * If dragging is not in progress, this value is undefined. + */ + int drag_current_y; + } guac_terminal_scrollbar; /** @@ -276,4 +294,29 @@ void guac_terminal_scrollbar_set_value(guac_terminal_scrollbar* scrollbar, void guac_terminal_scrollbar_parent_resized(guac_terminal_scrollbar* scrollbar, int parent_width, int parent_height, int visible_area); +/** + * Notifies the scrollbar of the current mouse state, allowing it to update + * itself with respect to button state and dragging. + * + * @param scrollbar + * The scrollbar to notify of the current mouse state. + * + * @param x + * The X coordinate of the mouse pointer. + * + * @param y + * The Y coordinate of the mouse pointer. + * + * @param mask + * The button mask, where the Nth bit of the button mask represents the + * pressed state of the Nth mouse button, where button 0 is the left + * mouse button, button 1 is the middle button, etc. + * + * @return + * Zero if the mouse event was not handled by the scrollbar, non-zero + * otherwise. + */ +int guac_terminal_scrollbar_handle_mouse(guac_terminal_scrollbar* scrollbar, + int x, int y, int mask); + #endif diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c index 7a1a9160..3c2f7f92 100644 --- a/src/terminal/terminal.c +++ b/src/terminal/terminal.c @@ -1379,17 +1379,28 @@ int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) { static int __guac_terminal_send_mouse(guac_terminal* term, int x, int y, int mask) { + guac_client* client = term->client; + guac_socket* socket = client->socket; + /* Determine which buttons were just released and pressed */ int released_mask = term->mouse_mask & ~mask; int pressed_mask = ~term->mouse_mask & mask; + /* Notify scrollbar, do not handle anything handled by scrollbar */ + if (guac_terminal_scrollbar_handle_mouse(term->scrollbar, x, y, mask)) { + guac_terminal_scrollbar_flush(term->scrollbar); + guac_protocol_send_sync(socket, client->last_sent_timestamp); + guac_socket_flush(socket); + return 0; + } + term->mouse_mask = mask; /* Show mouse cursor if not already shown */ if (term->current_cursor != term->ibar_cursor) { term->current_cursor = term->ibar_cursor; - guac_terminal_set_cursor(term->client, term->ibar_cursor); - guac_socket_flush(term->client->socket); + guac_terminal_set_cursor(client, term->ibar_cursor); + guac_socket_flush(socket); } /* Paste contents of clipboard on right or middle mouse button up */ @@ -1417,8 +1428,8 @@ static int __guac_terminal_send_mouse(guac_terminal* term, int x, int y, int mas free(string); /* Send data */ - guac_common_clipboard_send(term->clipboard, term->client); - guac_socket_flush(term->client->socket); + guac_common_clipboard_send(term->clipboard, client); + guac_socket_flush(socket); }