From 7eba65dccc5551faa6018ab01a6631da40e72233 Mon Sep 17 00:00:00 2001 From: Bruno Cabral Date: Tue, 19 Mar 2024 10:53:34 -0700 Subject: [PATCH] support for extra mouse buttons --- .gitignore | 1 + src/callbacks.c | 31 +++++++++++++----- src/config.c | 50 +++++++++++++++-------------- src/main.c | 84 +++++++++++++++++++++++++++++-------------------- src/main.h | 23 ++++++++++++-- 5 files changed, 122 insertions(+), 67 deletions(-) diff --git a/.gitignore b/.gitignore index d3b0feb..31df766 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ Makefile.in /flatpak/.flatpak-builder/ /flatpak/build-dir/ /.cache/ +/.vscode/ \ No newline at end of file diff --git a/src/callbacks.c b/src/callbacks.c index 2737f7b..f4d6556 100644 --- a/src/callbacks.c +++ b/src/callbacks.c @@ -270,13 +270,15 @@ gboolean on_buttonpress (GtkWidget *win, gdk_window_set_event_compression(gtk_widget_get_window(data->win), TRUE); } - /* See GdkModifierType. Am I fixing a Gtk misbehaviour??? */ - ev->state |= 1 << (ev->button + 7); + // add new buttons to GromitState + GromitState newState = devdata->state; + newState.buttons |= (ev->button <= 10) ? 1 << (ev->button - 1) : 0; + newState.modifiers = ev->state & 255; - if (ev->state != devdata->state || + if (!compare_state(devdata->state, newState) || devdata->lastslave != gdk_event_get_source_device ((GdkEvent *) ev)) - select_tool (data, ev->device, gdk_event_get_source_device ((GdkEvent *) ev), ev->state); + select_tool (data, ev->device, gdk_event_get_source_device ((GdkEvent *) ev), newState); GromitPaintType type = devdata->cur_context->type; @@ -301,7 +303,7 @@ gboolean on_buttonpress (GtkWidget *win, if(data->maxwidth > devdata->cur_context->maxwidth) data->maxwidth = devdata->cur_context->maxwidth; - if (ev->button <= 5) + if (ev->button <= 10) draw_line (data, ev->device, ev->x, ev->y, ev->x, ev->y); coord_list_prepend (data, ev->device, ev->x, ev->y, data->maxwidth); @@ -325,12 +327,22 @@ gboolean on_motion (GtkWidget *win, if (!devdata->is_grabbed) return FALSE; + // GdkEventMotion->state has only buttons 1-5, keep 6-10 + GromitState newState = devdata->state; + newState.buttons &= 992; // remove old 1-5, keep 6-10 + newState.buttons |= (ev->state >> 8) & 1023; // update new 1-5 + newState.modifiers = ev->state & 255; + + // return if there is no button pressed + if(!newState.buttons) + return TRUE; + if(data->debug) g_printerr("DEBUG: Device '%s': motion to (x,y)=(%.2f : %.2f)\n", gdk_device_get_name(ev->device), ev->x, ev->y); - if (ev->state != devdata->state || + if(!compare_state(devdata->state, newState) || devdata->lastslave != gdk_event_get_source_device ((GdkEvent *) ev)) - select_tool (data, ev->device, gdk_event_get_source_device ((GdkEvent *) ev), ev->state); + select_tool (data, ev->device, gdk_event_get_source_device ((GdkEvent *) ev), newState); GromitPaintType type = devdata->cur_context->type; @@ -455,6 +467,11 @@ gboolean on_buttonrelease (GtkWidget *win, (ev->y != devdata->lasty)) on_motion(win, (GdkEventMotion *) ev, user_data); + // remove released button bit from GromitState + guint button = 1 << (ev->button - 1); + devdata->state.buttons &= ~button; + devdata->state.modifiers = ev->state & 255; + if (!devdata->is_grabbed) return FALSE; diff --git a/src/config.c b/src/config.c index 87ea016..ff481c9 100644 --- a/src/config.c +++ b/src/config.c @@ -43,14 +43,13 @@ static gpointer UNDOKEY_SYMBOL_VALUE = (gpointer) 4; * Functions for parsing the Configuration-file */ -static gchar* parse_name (GScanner *scanner) +static gboolean parse_name (GScanner *scanner, GromitLookupKey *key) { GTokenType token; guint buttons = 0; guint modifier = 0; guint len = 0; - gchar *name; token = g_scanner_cur_token(scanner); @@ -58,11 +57,11 @@ static gchar* parse_name (GScanner *scanner) { g_scanner_unexp_token (scanner, G_TOKEN_STRING, NULL, NULL, NULL, "aborting", TRUE); - return NULL; + return 0; } len = strlen (scanner->value.v_string); - name = g_strndup (scanner->value.v_string, len + 3); + key->name = g_strndup (scanner->value.v_string, len); token = g_scanner_get_next_token (scanner); @@ -80,17 +79,17 @@ static gchar* parse_name (GScanner *scanner) { if (token == G_TOKEN_SYMBOL) { - if ((intptr_t) scanner->value.v_symbol < 11) + if ((intptr_t) scanner->value.v_symbol <= 10) buttons |= 1 << ((intptr_t) scanner->value.v_symbol - 1); else modifier |= 1 << ((intptr_t) scanner->value.v_symbol - 11); } else if (token == G_TOKEN_INT) { - if (scanner->value.v_int <= 5 && scanner->value.v_int > 0) + if (scanner->value.v_int <= 10 && scanner->value.v_int > 0) buttons |= 1 << (scanner->value.v_int - 1); else - g_printerr ("Only Buttons 1-5 are supported!\n"); + g_printerr ("Only Buttons 1-10 are supported!\n"); } else { @@ -102,12 +101,10 @@ static gchar* parse_name (GScanner *scanner) token = g_scanner_get_next_token (scanner); } - name [len] = 124; - name [len+1] = buttons + 64; - name [len+2] = modifier + 48; - name [len+3] = 0; + key->state.buttons = buttons; + key->state.modifiers = modifier; - return name; + return 1; } gboolean parse_config (GromitData *data) @@ -120,7 +117,7 @@ gboolean parse_config (GromitData *data) gchar *filename; int file; - gchar *name, *copy; + gboolean parsed; GromitPaintType type; GdkRGBA *fg_color=NULL; @@ -181,10 +178,15 @@ gboolean parse_config (GromitData *data) g_scanner_scope_add_symbol (scanner, 1, "BUTTON3", (gpointer) 3); g_scanner_scope_add_symbol (scanner, 1, "BUTTON4", (gpointer) 4); g_scanner_scope_add_symbol (scanner, 1, "BUTTON5", (gpointer) 5); + g_scanner_scope_add_symbol (scanner, 1, "BUTTON6", (gpointer) 6); + g_scanner_scope_add_symbol (scanner, 1, "BUTTON7", (gpointer) 7); + g_scanner_scope_add_symbol (scanner, 1, "BUTTON8", (gpointer) 8); + g_scanner_scope_add_symbol (scanner, 1, "BUTTON9", (gpointer) 9); + g_scanner_scope_add_symbol (scanner, 1, "BUTTON10", (gpointer) 10); g_scanner_scope_add_symbol (scanner, 1, "SHIFT", (gpointer) 11); - g_scanner_scope_add_symbol (scanner, 1, "CONTROL", (gpointer) 12); - g_scanner_scope_add_symbol (scanner, 1, "META", (gpointer) 13); - g_scanner_scope_add_symbol (scanner, 1, "ALT", (gpointer) 13); + g_scanner_scope_add_symbol (scanner, 1, "CONTROL", (gpointer) 13); + g_scanner_scope_add_symbol (scanner, 1, "META", (gpointer) 14); + g_scanner_scope_add_symbol (scanner, 1, "ALT", (gpointer) 14); g_scanner_scope_add_symbol (scanner, 2, "size", (gpointer) 1); g_scanner_scope_add_symbol (scanner, 2, "color", (gpointer) 2); @@ -206,10 +208,11 @@ gboolean parse_config (GromitData *data) /* * New tool definition */ + GromitLookupKey keyName = {}; - name = parse_name (scanner); + parsed = parse_name (scanner, &keyName); - if(!name) + if(!parsed) goto cleanup; token = g_scanner_cur_token(scanner); @@ -239,11 +242,12 @@ gboolean parse_config (GromitData *data) } else if (token == G_TOKEN_STRING) { - copy = parse_name (scanner); - if(!copy) + GromitLookupKey keyCopy = {}; + parsed = parse_name (scanner, &keyCopy); + if(!parsed) goto cleanup; token = g_scanner_cur_token(scanner); - context_template = g_hash_table_lookup (data->tool_config, copy); + context_template = g_hash_table_lookup (data->tool_config, key2string(keyCopy)); if (context_template) { type = context_template->type; @@ -257,7 +261,7 @@ gboolean parse_config (GromitData *data) else { g_printerr ("WARNING: Unable to copy \"%s\": " - "not yet defined!\n", copy); + "not yet defined!\n", key2string(keyCopy)); } } else @@ -430,7 +434,7 @@ gboolean parse_config (GromitData *data) context = paint_context_new (data, type, fg_color, width, arrowsize, arrowtype, minwidth, maxwidth); - g_hash_table_insert (data->tool_config, name, context); + g_hash_table_insert (data->tool_config, key2string(keyName), context); } else if (token == G_TOKEN_SYMBOL && (scanner->value.v_symbol == HOTKEY_SYMBOL_VALUE || diff --git a/src/main.c b/src/main.c index b54ccb7..f6d95c9 100644 --- a/src/main.c +++ b/src/main.c @@ -253,15 +253,13 @@ gint reshape (gpointer user_data) void select_tool (GromitData *data, GdkDevice *device, GdkDevice *slave_device, - guint state) + GromitState state) { guint buttons = 0, modifier = 0, slave_len = 0, len = 0, default_len = 0; guint req_buttons = 0, req_modifier = 0; guint i, j, success = 0; GromitPaintContext *context = NULL; - guchar *slave_name; - guchar *name; - guchar *default_name; + GromitLookupKey keySlave = {}, keyName = {}, keyDefault = {}; /* get the data for this device */ GromitDeviceData *devdata = g_hash_table_lookup(data->devdatatable, device); @@ -269,25 +267,16 @@ void select_tool (GromitData *data, if (device) { slave_len = strlen (gdk_device_get_name(slave_device)); - slave_name = (guchar*) g_strndup (gdk_device_get_name(slave_device), slave_len + 3); + keySlave.name = g_strndup (gdk_device_get_name(slave_device), slave_len); len = strlen (gdk_device_get_name(device)); - name = (guchar*) g_strndup (gdk_device_get_name(device), len + 3); + keyName.name = g_strndup (gdk_device_get_name(device), len); default_len = strlen(DEFAULT_DEVICE_NAME); - default_name = (guchar*) g_strndup (DEFAULT_DEVICE_NAME, default_len + 3); + keyDefault.name = g_strndup (DEFAULT_DEVICE_NAME, default_len); /* Extract Button/Modifiers from state (see GdkModifierType) */ - req_buttons = (state >> 8) & 31; - - req_modifier = (state >> 1) & 7; - if (state & GDK_SHIFT_MASK) req_modifier |= 1; - - slave_name [slave_len] = 124; - slave_name [slave_len+3] = 0; - name [len] = 124; - name [len+3] = 0; - default_name [default_len] = 124; - default_name [default_len+3] = 0; + req_buttons = state.buttons; + req_modifier = state.modifiers; /* Iterate i up until <= req_buttons. @@ -316,36 +305,36 @@ void select_tool (GromitData *data, { j++; modifier = req_modifier & ((1 << j)-1); - slave_name [slave_len+1] = buttons + 64; - slave_name [slave_len+2] = modifier + 48; - name [len+1] = buttons + 64; - name [len+2] = modifier + 48; - default_name [default_len+1] = buttons + 64; - default_name [default_len+2] = modifier + 48; + keySlave.state.buttons = buttons; + keySlave.state.modifiers = modifier; + keyName.state.buttons = buttons; + keyName.state.modifiers = modifier; + keyDefault.state.buttons = buttons; + keyDefault.state.modifiers = modifier; if(data->debug) - g_printerr("DEBUG: select_tool looking up context for '%s' attached to '%s'\n", slave_name, name); + g_printerr("DEBUG: select_tool looking up context for '%s' attached to '%s'\n", key2string(keySlave), key2string(keyName)); - context = g_hash_table_lookup (data->tool_config, slave_name); + context = g_hash_table_lookup (data->tool_config, key2string(keySlave)); if(context) { if(data->debug) - g_printerr("DEBUG: select_tool set context for '%s'\n", slave_name); + g_printerr("DEBUG: select_tool set context for '%s'\n", key2string(keySlave)); devdata->cur_context = context; success = 1; } else /* try master name */ - if ((context = g_hash_table_lookup (data->tool_config, name))) + if ((context = g_hash_table_lookup (data->tool_config, key2string(keyName)))) { if(data->debug) - g_printerr("DEBUG: select_tool set context for '%s'\n", name); + g_printerr("DEBUG: select_tool set context for '%s'\n", key2string(keyName)); devdata->cur_context = context; success = 1; } else /* try default_name */ - if((context = g_hash_table_lookup (data->tool_config, default_name))) + if((context = g_hash_table_lookup (data->tool_config, key2string(keyDefault)))) { if(data->debug) - g_printerr("DEBUG: select_tool set default context '%s' for '%s'\n", default_name, name); + g_printerr("DEBUG: select_tool set default context '%s' for '%s'\n", key2string(keyDefault), key2string(keyName)); devdata->cur_context = context; success = 1; } @@ -363,11 +352,9 @@ void select_tool (GromitData *data, devdata->cur_context = data->default_pen; if(data->debug) - g_printerr("DEBUG: select_tool set fallback context for '%s'\n", name); + g_printerr("DEBUG: select_tool set fallback context for '%s'\n", key2string(keyName)); } - g_free (name); - g_free (default_name); } else g_printerr ("ERROR: select_tool attempted to select nonexistent device!\n"); @@ -1189,3 +1176,32 @@ void indicate_active(GromitData *data, gboolean YESNO) else app_indicator_set_icon(data->trayicon, "net.christianbeier.Gromit-MPX"); } + +gboolean compare_state(GromitState lhs, GromitState rhs) +{ + return lhs.buttons == rhs.buttons && + lhs.modifiers == rhs.modifiers; +} + +gchar *key2string(GromitLookupKey key) +{ + guint len = 0; + gchar *result; + + len = strlen(key.name); + result = g_strndup(key.name, len + 4); + + result[len] = 124; + + // to identify buttons 1-10 we need two bytes (two char) + guint buttons = key.state.buttons; + gchar buttons_low = key.state.buttons & 255; // 1-8 + gchar buttons_high = key.state.buttons >> 8 & 255; // 9-10 + + result[len + 1] = buttons_high + 48; + result[len + 2] = buttons_low + 48; + result[len + 3] = key.state.modifiers + 48; + result[len + 4] = 0; + + return result; +} diff --git a/src/main.h b/src/main.h index 8fea9ba..3d1e340 100644 --- a/src/main.h +++ b/src/main.h @@ -37,7 +37,8 @@ #define GROMIT_MOUSE_EVENTS ( GDK_BUTTON_MOTION_MASK | \ GDK_BUTTON_PRESS_MASK | \ - GDK_BUTTON_RELEASE_MASK ) + GDK_BUTTON_RELEASE_MASK | \ + GDK_POINTER_MOTION_MASK ) #define GROMIT_WINDOW_EVENTS ( GROMIT_MOUSE_EVENTS | GDK_EXPOSURE_MASK) @@ -90,6 +91,19 @@ typedef struct gdouble pressure; } GromitPaintContext; + +typedef struct { + guint buttons; + guint modifiers; +} GromitState; + + +typedef struct { + GromitState state; + gchar *name; +} GromitLookupKey; + + typedef struct { gdouble lastx; @@ -98,7 +112,7 @@ typedef struct GList* coordlist; GdkDevice* device; guint index; - guint state; + GromitState state; GromitPaintContext *cur_context; gboolean is_grabbed; gboolean was_grabbed; @@ -168,7 +182,7 @@ void show_window (GromitData *data); void parse_print_help (gpointer key, gpointer value, gpointer user_data); -void select_tool (GromitData *data, GdkDevice *device, GdkDevice *slave_device, guint state); +void select_tool (GromitData *data, GdkDevice *device, GdkDevice *slave_device, GromitState state); void copy_surface (cairo_surface_t *dst, cairo_surface_t *src); void swap_surfaces (cairo_surface_t *a, cairo_surface_t *b); @@ -186,4 +200,7 @@ void paint_context_free (GromitPaintContext *context); void indicate_active(GromitData *data, gboolean YESNO); +gboolean compare_state(GromitState lhs, GromitState rhs); +gchar *key2string(GromitLookupKey key); + #endif