Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package wofi for openSUSE:Factory checked in at 2024-02-12 18:53:03 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/wofi (Old) and /work/SRC/openSUSE:Factory/.wofi.new.1815 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "wofi" Mon Feb 12 18:53:03 2024 rev:6 rq:1146099 version:1.4 Changes: -------- --- /work/SRC/openSUSE:Factory/wofi/wofi.changes 2022-11-29 13:23:36.158059701 +0100 +++ /work/SRC/openSUSE:Factory/.wofi.new.1815/wofi.changes 2024-02-12 18:55:26.985518590 +0100 @@ -1,0 +2,27 @@ +Thu Feb 8 18:22:13 UTC 2024 - Jan Baier <jba...@suse.com> + +- update to 1.4: + * Added the config option drun-disable_prime to disable prime GPU offloading + * Updated meson command in readme + * Added wezterm to the default terminal list and moved termite to the end of the sane terminal section since it's now deprecated + * Updated the man pages with the new terminal order + * Refactor key mapping modifier logic + * Add return codes for custom keys + * on_exit_set_custom_key_return_code() now flushes stdio, this fixes #197 + * Fixed #193. drun mode now respects Hidden + * Add `#expander` selector so expanded actions can be themed + * Rename `#expander` to `#expander-box` + * Added documentation for key_custom_n + * Fixed bug reported in https://github.com/swaywm/sway/issues/7915 + * Fixed malloc for pre_display_exec + * Forgot to cleanup zombie processes + * Fixed closing the file handle when pre_display_exec=true + * Added pre_display_exec which allows pre_display_cmd to be directly executed with fork/exec instead of through the shell + * Fixed segfault when running non shell safe inputs with --pre-display-cmd and --allow-images + * Added the drun-print_desktop_file option + * Fixed weird tabbing behavior + * Fixed indentation + * Make sure that the first calculation of percent size is done when the window is visible + * Add documentation for `#expander-box` + +------------------------------------------------------------------- Old: ---- v1.3.tar.gz New: ---- v1.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ wofi.spec ++++++ --- /var/tmp/diff_new_pack.O3KfVQ/_old 2024-02-12 18:55:27.429534624 +0100 +++ /var/tmp/diff_new_pack.O3KfVQ/_new 2024-02-12 18:55:27.433534769 +0100 @@ -1,7 +1,7 @@ # # spec file for package wofi # -# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: wofi -Version: 1.3 +Version: 1.4 Release: 0 Summary: Launcher for wlroots compositors License: GPL-3.0-only ++++++ v1.3.tar.gz -> v1.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wofi-v1.3/.hg_archival.txt new/wofi-v1.4/.hg_archival.txt --- old/wofi-v1.3/.hg_archival.txt 2022-10-07 04:35:13.000000000 +0200 +++ new/wofi-v1.4/.hg_archival.txt 2024-02-05 00:26:21.000000000 +0100 @@ -1,4 +1,4 @@ repo: 1c71dcd9c6a6dd54601820ce069e7c3ed7e946ca -node: 1c32143a8460e01559e141c291500a6f4ddcf18c +node: eab2b31e805564012e4f71920a8f87ba5f9f798c branch: default -tag: v1.3 +tag: v1.4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wofi-v1.3/.hgtags new/wofi-v1.4/.hgtags --- old/wofi-v1.3/.hgtags 2022-10-07 04:35:13.000000000 +0200 +++ new/wofi-v1.4/.hgtags 2024-02-05 00:26:21.000000000 +0100 @@ -7,3 +7,4 @@ b352d73b652aadd3b9da064267d6f3fa26007c25 v1.2.2 e208549963dcd4ae89a18290aa598814c0b8eeb7 v1.2.3 84e91980936bf85a854cee6881398cff9d27fce4 v1.2.4 +1c32143a8460e01559e141c291500a6f4ddcf18c v1.3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wofi-v1.3/README.md new/wofi-v1.4/README.md --- old/wofi-v1.3/README.md 2022-10-07 04:35:13.000000000 +0200 +++ new/wofi-v1.4/README.md 2024-02-05 00:26:21.000000000 +0100 @@ -5,12 +5,12 @@ ## Dependencies libwayland-dev libgtk-3-dev - pkg-config + pkgconf meson ## Building hg clone https://hg.sr.ht/~scoopta/wofi cd wofi - meson build + meson setup build ninja -C build ## Installing sudo ninja -C build install @@ -31,21 +31,8 @@ dbus-daemon --session --address=unix:path=$XDG_RUNTIME_DIR/bus ## Packages -Debian sid has a package in the official repos https://packages.debian.org/sid/wofi +[](https://repology.org/project/wofi/versions) -Ubuntu focal has a package in universe https://packages.ubuntu.com/focal/wofi - -Arch has an AUR package for the current tip https://aur.archlinux.org/packages/wofi-hg/ and an official package for the current stable https://www.archlinux.org/packages/community/x86_64/wofi/ - -NixOS has a packge in unstable https://nixos.org/nixos/packages.html?attr=wofi&channel=nixos-unstable&query=wofi - -Void Linux also has a package - -Fedora has an official package https://src.fedoraproject.org/rpms/wofi as well as one in COPR https://copr.fedorainfracloud.org/coprs/wef/wofi/ - -FreeBSD has an official package https://www.freebsd.org/cgi/ports.cgi?query=wofi&stype=all&sektion=x11 - -Gentoo has a portage overlay https://gpo.zugaina.org/Overlays/guru/gui-apps/wofi ## Documentation The official documentation is provided by the man pages in this repository, sample styling can be found here https://cloudninja.pw/docs/wofi.html diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wofi-v1.3/man/wofi-api.3 new/wofi-v1.4/man/wofi-api.3 --- old/wofi-v1.3/man/wofi-api.3 2022-10-07 04:35:13.000000000 +0200 +++ new/wofi-v1.4/man/wofi-api.3 2024-02-05 00:26:21.000000000 +0100 @@ -106,7 +106,7 @@ .TP .B void wofi_term_run(const char* cmd) -Runs the provided cmd in a terminal emulator. The following order is used for picking a terminal emulator: The user specified terminal, kitty, termite, alacritty, foot, gnome\-terminal, weston\-terminal. If none of these can be found execution will fail. +Runs the provided cmd in a terminal emulator. The following order is used for picking a terminal emulator: The user specified terminal, kitty, alacritty, wezterm, foot, termite, gnome\-terminal, weston\-terminal. If none of these can be found execution will fail. .B const char* cmd \- The command to run, this is invoked by doing \fBterm \-\- cmd\fR. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wofi-v1.3/man/wofi-keys.7 new/wofi-v1.4/man/wofi-keys.7 --- old/wofi-v1.3/man/wofi-keys.7 2022-10-07 04:35:13.000000000 +0200 +++ new/wofi-v1.4/man/wofi-keys.7 2024-02-05 00:26:21.000000000 +0100 @@ -3,7 +3,7 @@ wofi \- Key names for custom binds .SH DESCRIPTION -This is a list of the key names that can be used for custom binding, these are taken from gdk/gdkkeysyms.h. Certain keys cannot have the shift modifier attached as holding shift while using these keys causes a completely different key press to be registered. For example Shift_L\-j is invalid as holding shift while pressing j changes the key into J so J should be registered as the key instead of Shift_L\-j. This is the case with all alphanumeric chars as well as Tab which turns into ISO_Left_Tab. +This is a list of the key names that can be used for custom binding. These are taken from gdk/gdkkeysyms.h with exception to modifiers. Certain keys cannot have the shift modifier attached as holding shift while using these keys causes a completely different key press to be registered. For example Shift\-j is invalid as holding shift while pressing j changes the key into J so Shift-J should be registered as the key instead of Shift\-j. This is the case with all alphanumeric chars as well as Tab which turns into ISO_Left_Tab. .SH KEY NAMES .B BackSpace @@ -4543,3 +4543,10 @@ .B LogWindowTree .br .B LogGrabInfo + +.SH MODIFIER NAMES +.B Shift +.br +.B Ctrl +.br +.B Alt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wofi-v1.3/man/wofi.1 new/wofi-v1.4/man/wofi.1 --- old/wofi-v1.3/man/wofi.1 2022-10-07 04:35:13.000000000 +0200 +++ new/wofi-v1.4/man/wofi.1 2024-02-05 00:26:21.000000000 +0100 @@ -63,7 +63,7 @@ Specifies the cache file to load/store cache, default is $XDG_CACHE_HOME/wofi\-<mode name> where <mode name> is the name of the mode, if $XDG_CACHE_HOME is not specified ~/.cache is used. .TP .B \-t, \-\-term=\fITERM\fR -Specifies the term to use when running a program in a terminal. This overrides the default terminal run order which is kitty, termite, alacritty, foot, gnome\-terminal, weston\-terminal in that order. +Specifies the term to use when running a program in a terminal. This overrides the default terminal run order which is kitty, alacritty, wezterm, foot, termite, gnome\-terminal, weston\-terminal in that order. .TP .B \-P, \-\-password \fR[character] Runs wofi in password mode with an optional password character to use. If no character is specified * is used by default. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wofi-v1.3/man/wofi.5 new/wofi-v1.4/man/wofi.5 --- old/wofi-v1.3/man/wofi.5 2022-10-07 04:35:13.000000000 +0200 +++ new/wofi-v1.4/man/wofi.5 2024-02-05 00:26:21.000000000 +0100 @@ -65,7 +65,7 @@ Specifies the cache file to load/store cache, default is $XDG_CACHE_HOME/wofi\-<mode name> where <mode name> is the name of the mode, if $XDG_CACHE_HOME is not specified ~/.cache is used. .TP .B term=\fITERM\fR -Specifies the term to use when running a program in a terminal. This overrides the default terminal run order which is kitty, termite, alacritty, foot, gnome\-terminal, weston\-terminal in that order. +Specifies the term to use when running a program in a terminal. This overrides the default terminal run order which is kitty, alacritty, wezterm, foot, termite, gnome\-terminal, weston\-terminal in that order. .TP .B password=\fICHARACTER\fR Runs wofi in password mode using the specified character, default is false. @@ -167,7 +167,10 @@ Specifies the key to use in order to hide/show the search bar. There is no default. See \fBwofi\-keys\fR(7) for the key codes. .TP .B key_copy=\fIKEY\fR -Specifies the key to use in order to copy the action text for the current entry. The default is Control_L-c. See \fBwofi\-keys\fR(7) for the key codes. +Specifies the key to use in order to copy the action text for the current entry. The default is Ctrl-c. See \fBwofi\-keys\fR(7) for the key codes. +.TP +.B key_custom_(n)=\fIKEY\fR +Allows for configuring custom exit codes. For example setting key_custom_0=Ctrl-0 will make it so if you press Ctrl-0 wofi will set its exit status to 10. This will not cause wofi to exit, it will only set its exit code for when it does. 20 of these keys are configurable numbered 0-19. The exit status used is 10+n where n is the number attached to key_custom_n. There are no defaults for these. See \fBwofi\-keys\fR(7) for the key codes. .TP .B line_wrap=\fIMODE\fR Specifies the line wrap mode to use. The options are off, word, char, and word_char. Default is off. @@ -189,6 +192,9 @@ .TP .B single_click=\fIBOOL\fR Specifies whether or not actions should be executed on a single click or a double click. Default is false. +.TP +.B pre_display_exec=\fIBOOL\fR +This modifies the behavior of pre_display_cmd and causes the command in question to be directly executed via fork/exec rather than through the shell. .SH CSS SELECTORS Any GTK widget can be selected by using the name of its CSS node, these however might change with updates and are not guaranteed to stay constant. Wofi also provides certain widgets with names and classes which can be referenced from CSS to give access to the most important widgets easily. \fBwofi\fR(7) contains the current widget layout used by wofi so if you want to get into CSS directly using GTK widget names look there for info. @@ -237,6 +243,10 @@ .B #entry .br The name of all entries. +.TP +.B #expander-box +.br +The name of all boxes shown when expanding entries with multiple actions .SH COLORS The colors file should be formatted as new line separated hex values. These values should be in the standard HTML format and begin with a hash. These colors will be loaded however wofi doesn't know what color should be used for what so you must reference them from your CSS. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wofi-v1.3/man/wofi.7 new/wofi-v1.4/man/wofi.7 --- old/wofi-v1.3/man/wofi.7 2022-10-07 04:35:13.000000000 +0200 +++ new/wofi-v1.4/man/wofi.7 2024-02-05 00:26:21.000000000 +0100 @@ -53,6 +53,12 @@ .TP .B display_generic=\fIBOOL\fR If true then generic names will be displayed in () next to the application name, default is false. +.TP +.B disable_prime=\fIBOOL\fR +If true then wofi will ignore the PrefersNonDefaultGPU desktop variable, specifically this prevents wofi from setting DRI_PRIME, default is false. +.TP +.B print_desktop_file=\fIBOOL\fR +If true the path to the desktop file and the name of the corresponding action(if present) will be printed to stdout instead of invoking it, default is false. .SH DRUN When images are enabled drun mode will pull icon themes however being a GTK app it's possible you'll need to run gtk\-update\-icon\-cache to get them to apply. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wofi-v1.3/meson.build new/wofi-v1.4/meson.build --- old/wofi-v1.3/meson.build 2022-10-07 04:35:13.000000000 +0200 +++ new/wofi-v1.4/meson.build 2024-02-05 00:26:21.000000000 +0100 @@ -1,4 +1,4 @@ -project('wofi', 'c', version : 'v1.3', default_options : ['c_std=c99', 'buildtype=release', 'warning_level=2']) +project('wofi', 'c', version : 'v1.4', default_options : ['c_std=c99', 'buildtype=release', 'warning_level=2']) cc = meson.get_compiler('c') pkgcfg = import('pkgconfig') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wofi-v1.3/modes/drun.c new/wofi-v1.4/modes/drun.c --- old/wofi-v1.3/modes/drun.c 2022-10-07 04:35:13.000000000 +0200 +++ new/wofi-v1.4/modes/drun.c 2024-02-05 00:26:21.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2022 Scoopta + * Copyright (C) 2019-2023 Scoopta * This file is part of Wofi * Wofi is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ #include <gtk/gtk.h> #include <gio/gdesktopappinfo.h> -static const char* arg_names[] = {"print_command", "display_generic"}; +static const char* arg_names[] = {"print_command", "display_generic", "disable_prime", "print_desktop_file"}; static struct mode* mode; @@ -43,6 +43,8 @@ static bool print_command; static bool display_generic; +static bool disable_prime; +static bool print_desktop_file; static char* get_search_text(char* file) { GDesktopAppInfo* info = g_desktop_app_info_new_from_filename(file); @@ -75,7 +77,8 @@ static bool populate_widget(char* file, char* action, struct widget_builder* builder) { GDesktopAppInfo* info = g_desktop_app_info_new_from_filename(file); - if(info == NULL || !g_app_info_should_show(G_APP_INFO(info))) { + if(info == NULL || !g_app_info_should_show(G_APP_INFO(info)) || + g_desktop_app_info_get_is_hidden(info)) { return false; } const char* name; @@ -335,6 +338,8 @@ print_command = strcmp(config_get(config, "print_command", "false"), "true") == 0; display_generic = strcmp(config_get(config, "display_generic", "false"), "true") == 0; + disable_prime = strcmp(config_get(config, "disable_prime", "false"), "true") == 0; + print_desktop_file = strcmp(config_get(config, "print_desktop_file", "false"), "true") == 0; entries = map_init(); struct wl_list* cache = wofi_read_cache(mode); @@ -407,7 +412,7 @@ static void set_dri_prime(GDesktopAppInfo* info) { bool dri_prime = g_desktop_app_info_get_boolean(info, "PrefersNonDefaultGPU"); - if(dri_prime) { + if(dri_prime && !disable_prime) { setenv("DRI_PRIME", "1", true); } } @@ -445,6 +450,9 @@ printf("%s\n", cmd); free(cmd); exit(0); + } else if(print_desktop_file) { + printf("%s\n", cmd); + exit(0); } else { set_dri_prime(info); if(uses_dbus(info)) { @@ -465,6 +473,9 @@ printf("%s\n", cmd); free(cmd); fprintf(stderr, "Printing the command line for an action is not supported\n"); + } else if(print_desktop_file) { + printf("%s %s\n", cmd, action); + exit(0); } else { set_dri_prime(info); g_desktop_app_info_launch_action(info, action, NULL); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wofi-v1.3/src/wofi.c new/wofi-v1.4/src/wofi.c --- old/wofi-v1.3/src/wofi.c 2022-10-07 04:35:13.000000000 +0200 +++ new/wofi-v1.4/src/wofi.c 2024-02-05 00:26:21.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2022 Scoopta + * Copyright (C) 2019-2024 Scoopta * This file is part of Wofi * Wofi is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,8 +42,10 @@ #include <gdk/gdkwayland.h> #define PROTO_VERSION(v1, v2) (v1 < v2 ? v1 : v2) +#define _UNUSED(x) (void)(x) +#define CUSTOM_KEY_NUMBER 20 -static const char* terminals[] = {"kitty", "termite", "alacritty", "foot", "gnome-terminal", "weston-terminal"}; +static const char* terminals[] = {"kitty", "alacritty", "wezterm", "foot", "termite", "gnome-terminal", "weston-terminal"}; enum location { LOCATION_CENTER, @@ -105,9 +107,14 @@ static bool has_joined_mode = false; static char* copy_exec = NULL; static char* pre_display_cmd = NULL; +static bool pre_display_exec = false; static bool single_click = false; +static GdkModifierType shift_mask = GDK_SHIFT_MASK; +static GdkModifierType ctrl_mask = GDK_CONTROL_MASK; +static GdkModifierType alt_mask = GDK_MOD1_MASK; static struct map* keys; +static struct map* mods; static struct wl_display* wl = NULL; static struct wl_surface* wl_surface; @@ -127,6 +134,8 @@ void (*action)(void); }; +static int custom_key_return_code = EXIT_SUCCESS; + static void nop() {} static void add_interface(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { @@ -260,6 +269,9 @@ map_put(mode_map, "img-base64-noscale", "true"); map_put(mode_map, "text", "true"); + if(text == NULL) { + return NULL; + } char* original = strdup(text); char* mode1 = NULL; char* mode2 = NULL; @@ -404,29 +416,87 @@ char line[128]; // you'd think this caps the line's length to 128, but it's just a buffer which due to the nature of fgets() splits on lines size_t size = 0; // first, prepare cmd_labeltext to be each entry's actual comamand to run, aka replacing 'cat %s' to be 'cat filename' - if ((asprintf(&cmd_labeltext, pre_display_cmd, nodetext)) == -1) { + if(asprintf(&cmd_labeltext, pre_display_cmd, nodetext) == -1) { fprintf(stderr, "error parsing pre_display_cmd to run\n"); exit(EXIT_FAILURE); } // then, run the command - fp_labeltext = popen(cmd_labeltext, "r"); - if (fp_labeltext == NULL) { + if(pre_display_exec) { + int fds[2]; + if(pipe(fds) == -1) { + perror("pipe broken"); + exit(1); + } + if(fork() == 0) { + close(fds[0]); + dup2(fds[1], STDOUT_FILENO); + + char* cmd = strdup(pre_display_cmd); + char* space = strchr(cmd, ' '); + if(space != NULL) { + *space = 0; + } + + + size_t space_count = 0; + char* tmp_space = space; + for(; (tmp_space = strchr(tmp_space + 1, ' ')) != NULL; ++space_count); + + char** args = malloc((sizeof(char*) * (space_count + 2))); + args[0] = cmd; + args[1] = space + 1; + args[space_count + 2] = NULL; + + //> 0 is used because args[0] is the command + for(size_t count = space_count; count > 0; --count) { + char* arg = strrchr(space + 1, ' '); + args[count + 1] = arg + 1; + *arg = 0; + } + + for(size_t count = 1; count <= space_count + 1; ++count) { + if(strstr(args[count], "%s") != NULL) { + if(asprintf(&args[count], args[count], nodetext) == -1) { + fprintf(stderr, "error parsing pre_display_cmd to run\n"); + exit(EXIT_FAILURE); + } + } + } + + execvp(cmd, args); + free(cmd); + free(args); + fprintf(stderr, "error executing '%s'\n", cmd_labeltext); + exit(1); + } + close(fds[1]); + + fp_labeltext = fdopen(fds[0], "r"); + } else { + fp_labeltext = popen(cmd_labeltext, "r"); + } + if(fp_labeltext == NULL) { fprintf(stderr, "error executing '%s'\n", cmd_labeltext); exit(EXIT_FAILURE); - } else if (fgets(line, sizeof(line), fp_labeltext) != NULL) { + } else if(fgets(line, sizeof(line), fp_labeltext) != NULL) { // lastly, read the output of said command, and put it into the text variable to be used for the label widgets // consider using 'printf %.10s as your --pre-display-cmd to limit a string to a determined width. 10 here is an example size += strlen(line)+1; // we need place for the \0 of strcpy text = (char *) realloc(text, size); strcpy(text, line); - while (fgets(line, sizeof(line), fp_labeltext) != NULL) { + while(fgets(line, sizeof(line), fp_labeltext) != NULL) { size += strlen(line); text = (char *) realloc(text, size); strncat(text, line, size); } } - pclose(fp_labeltext); + if(pre_display_exec) { + fclose(fp_labeltext); + while(waitpid(-1, NULL, WNOHANG) > 0); + } else { + pclose(fp_labeltext); + } } if(allow_images) { @@ -584,6 +654,7 @@ gtk_expander_set_label_widget(GTK_EXPANDER(parent), box); GtkWidget* exp_box = gtk_list_box_new(); + gtk_widget_set_name(exp_box, "expander-box"); gtk_list_box_set_activate_on_single_click(GTK_LIST_BOX(exp_box), single_click); g_signal_connect(exp_box, "row-activated", G_CALLBACK(activate_item), NULL); gtk_container_add(GTK_CONTAINER(parent), exp_box); @@ -1051,29 +1122,48 @@ } static void select_first(void) { - GtkFlowBoxChild* child = gtk_flow_box_get_child_at_index(GTK_FLOW_BOX(inner_box), 1); + GtkFlowBoxChild* child = gtk_flow_box_get_child_at_index(GTK_FLOW_BOX(inner_box), 0); gtk_widget_grab_focus(GTK_WIDGET(child)); gtk_flow_box_select_child(GTK_FLOW_BOX(inner_box), GTK_FLOW_BOX_CHILD(child)); } -static GdkModifierType get_mask_from_keyval(guint keyval) { - switch(keyval) { - case GDK_KEY_Shift_L: - case GDK_KEY_Shift_R: +static GdkModifierType get_mask_from_keystate(guint state) { + if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) { return GDK_SHIFT_MASK; - case GDK_KEY_Control_L: - case GDK_KEY_Control_R: + } + + if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) { return GDK_CONTROL_MASK; - case GDK_KEY_Alt_L: - case GDK_KEY_Alt_R: + } + + // Alt + if ((state & GDK_MOD1_MASK) == GDK_MOD1_MASK) { return GDK_MOD1_MASK; + } + + return 0; +} + +static char* get_name_from_mask(GdkModifierType mask) { + switch(mask) { + case GDK_SHIFT_MASK: + return "Shift"; + case GDK_CONTROL_MASK: + return "Ctrl"; + case GDK_MOD1_MASK: + return "Alt"; default: - return 0; + return NULL; } } static GdkModifierType get_mask_from_name(char* name) { - return get_mask_from_keyval(gdk_keyval_from_name(name)); + GdkModifierType* mask = map_get(mods, name); + if (mask) { + return *mask; + } + + return 0; } static void move_up(void) { @@ -1114,12 +1204,21 @@ select_first(); return; } + gtk_widget_child_focus(window, GTK_DIR_TAB_FORWARD); + + if(gtk_widget_has_focus(entry)) { + select_first(); + } } static void move_backward(void) { user_moved = true; gtk_widget_child_focus(window, GTK_DIR_TAB_BACKWARD); + + if(gtk_widget_has_focus(entry)) { + move_backward(); + } } static void move_pgup(void) { @@ -1200,7 +1299,43 @@ } } +static void on_exit_set_custom_key_return_code(int status, void* data) { + _UNUSED(data); + if (status == EXIT_SUCCESS) { + fflush(stdout); + fflush(stderr); + _exit(custom_key_return_code); + } +} + +static void do_custom_key(int custom_key_num) { + custom_key_return_code = custom_key_num + 10; +} + +static void do_custom_key_0(void) {do_custom_key(0);} +static void do_custom_key_1(void) {do_custom_key(1);} +static void do_custom_key_2(void) {do_custom_key(2);} +static void do_custom_key_3(void) {do_custom_key(3);} +static void do_custom_key_4(void) {do_custom_key(4);} +static void do_custom_key_5(void) {do_custom_key(5);} +static void do_custom_key_6(void) {do_custom_key(6);} +static void do_custom_key_7(void) {do_custom_key(7);} +static void do_custom_key_8(void) {do_custom_key(8);} +static void do_custom_key_9(void) {do_custom_key(9);} +static void do_custom_key_10(void) {do_custom_key(10);} +static void do_custom_key_11(void) {do_custom_key(11);} +static void do_custom_key_12(void) {do_custom_key(12);} +static void do_custom_key_13(void) {do_custom_key(13);} +static void do_custom_key_14(void) {do_custom_key(14);} +static void do_custom_key_15(void) {do_custom_key(15);} +static void do_custom_key_16(void) {do_custom_key(16);} +static void do_custom_key_17(void) {do_custom_key(17);} +static void do_custom_key_18(void) {do_custom_key(18);} +static void do_custom_key_19(void) {do_custom_key(19);} + static bool do_key_action(GdkEvent* event, char* mod, void (*action)(void)) { + // GTK children focus gets all messed up if we don't first blow away any + // modifier keystate that's currently happening. if(mod != NULL) { GdkModifierType mask = get_mask_from_name(mod); if((event->key.state & mask) == mask) { @@ -1280,9 +1415,24 @@ return FALSE; } - bool key_success = true; - struct key_entry* key_ent = map_get(keys, gdk_keyval_name(event->key.keyval)); + struct key_entry* key_ent = NULL; + char* mod = NULL; + + if (has_mod(event->key.state)) { + char* keyval = gdk_keyval_name(event->key.keyval); + GdkModifierType mask = get_mask_from_keystate(event->key.state); + mod = get_name_from_mask(mask); + size_t len = strlen(mod) + 1 + strlen(keyval) + 1; + char* mod_keyval = malloc(len); + strcpy(mod_keyval, mod); + strcat(mod_keyval, "-"); + strcat(mod_keyval, keyval); + key_ent = map_get(keys, mod_keyval); + free(mod_keyval); + } else { + key_ent = map_get(keys, gdk_keyval_name(event->key.keyval)); + } if(key_ent != NULL && key_ent->action != NULL) { key_success = do_key_action(event, key_ent->mod, key_ent->action); @@ -1486,7 +1636,7 @@ return NULL; } -static void parse_mods(char* key, void (*action)(void)) { +static void add_key_entry(char* key, void (*action)(void)) { char* tmp = strdup(key); char* save_ptr; char* str = strtok_r(tmp, ",", &save_ptr); @@ -1495,22 +1645,20 @@ break; } char* hyphen = strchr(str, '-'); - char* mod; + char* mod = NULL; + GdkModifierType modifier = 0; if(hyphen != NULL) { - *hyphen = 0; - guint key1 = gdk_keyval_from_name(str); - guint key2 = gdk_keyval_from_name(hyphen + 1); - if(get_mask_from_keyval(key1) != 0) { + *hyphen = '\0'; + modifier = get_mask_from_name(str); + if (modifier) { mod = str; str = hyphen + 1; - } else if(get_mask_from_keyval(key2) != 0) { - mod = hyphen + 1; } else { - fprintf(stderr, "Neither %s nor %s is a modifier, this is not supported\n", str, hyphen + 1); - mod = NULL; + fprintf(stderr, + "Could not parse %s as a modifier. Valid modifiers are " + "Ctrl, Shift, and Alt.\n", + str); } - } else { - mod = NULL; } struct key_entry* entry = malloc(sizeof(struct key_entry)); if(mod == NULL) { @@ -1519,7 +1667,20 @@ entry->mod = strdup(mod); } entry->action = action; - map_put_void(keys, str, entry); + char* map_key = NULL; + + if (mod) { + size_t len = strlen(mod) + 1 + strlen(str) + 1; + map_key = malloc(len); + strcpy(map_key, mod); + strcat(map_key, "-"); + strcat(map_key, str); + } else { + map_key = strdup(str); + } + + map_put_void(keys, map_key, entry); + free(map_key); } while((str = strtok_r(NULL, ",", &save_ptr)) != NULL); free(tmp); } @@ -1572,12 +1733,24 @@ return G_SOURCE_CONTINUE; } +static gboolean do_percent_size_first(gpointer data){ + gdk_threads_add_timeout(50, do_percent_size, data); + do_percent_size(data); + return G_SOURCE_REMOVE; +} + void wofi_init(struct map* _config) { config = _config; char* width_str = config_get(config, "width", "50%"); char* height_str = config_get(config, "height", "40%"); width = strtol(width_str, NULL, 10); height = strtol(height_str, NULL, 10); + + if(width > UINT16_MAX || height > UINT16_MAX) { + fprintf(stderr, "Do you actually have a monitor big enough to see this O_o? Dimensions can be no larger than %ux%u\n", UINT16_MAX, UINT16_MAX); + exit(1); + } + x = map_get(config, "x"); y = map_get(config, "y"); bool normal_window = strcmp(config_get(config, "normal_window", "false"), "true") == 0; @@ -1622,39 +1795,94 @@ char* layer = config_get(config, "layer", "top"); copy_exec = config_get(config, "copy_exec", "wl-copy"); pre_display_cmd = map_get(config, "pre_display_cmd"); + pre_display_exec = strcmp(config_get(config, "pre_display_exec", "false"), "true") == 0; single_click = strcmp(config_get(config, "single_click", "false"), "true") == 0; keys = map_init_void(); + mods = map_init_void(); - - - char* key_up = config_get(config, "key_up", "Up"); - char* key_down = config_get(config, "key_down", "Down"); - char* key_left = config_get(config, "key_left", "Left"); - char* key_right = config_get(config, "key_right", "Right"); - char* key_forward = config_get(config, "key_forward", "Tab"); - char* key_backward = config_get(config, "key_backward", "ISO_Left_Tab"); - char* key_submit = config_get(config, "key_submit", "Return"); - char* key_exit = config_get(config, "key_exit", "Escape"); - char* key_pgup = config_get(config, "key_pgup", "Page_Up"); - char* key_pgdn = config_get(config, "key_pgdn", "Page_Down"); - char* key_expand = config_get(config, "key_expand", ""); - char* key_hide_search = config_get(config, "key_hide_search", ""); - char* key_copy = config_get(config, "key_copy", "Control_L-c"); - - parse_mods(key_up, move_up); - parse_mods(key_down, move_down); - parse_mods(key_left, move_left); - parse_mods(key_right, move_right); - parse_mods(key_forward, move_forward); - parse_mods(key_backward, move_backward); - parse_mods(key_submit, NULL); //submit is a special case, when a NULL action is encountered submit is used instead - parse_mods(key_exit, do_exit); - parse_mods(key_pgup, move_pgup); - parse_mods(key_pgdn, move_pgdn); - parse_mods(key_expand, do_expand); - parse_mods(key_hide_search, do_hide_search); - parse_mods(key_copy, do_copy); + map_put_void(mods, "Shift", &shift_mask); + map_put_void(mods, "Ctrl", &ctrl_mask); + map_put_void(mods, "Alt", &alt_mask); + + // First pass maps the default key entries. + // Second pass maps any custom key entry configs in addition to (or + // replacing) defaults. + for (int i = 0; i < 2; i++) { + char* key_default; + + key_default = "Up"; + char* key_up = (i == 0) ? "Up" : config_get(config, "key_up", key_default); + key_default = "Down"; + char* key_down = (i == 0) ? key_default : config_get(config, "key_down", key_default); + key_default = "Left"; + char* key_left = (i == 0) ? key_default : config_get(config, "key_left", key_default); + key_default = "Right"; + char* key_right = (i == 0) ? key_default : config_get(config, "key_right", key_default); + key_default = "Tab"; + char* key_forward = (i == 0) ? key_default : config_get(config, "key_forward", key_default); + key_default = "Shift-ISO_Left_Tab"; + char* key_backward = (i == 0) ? key_default : config_get(config, "key_backward", key_default); + key_default = "Return"; + char* key_submit = (i == 0) ? key_default : config_get(config, "key_submit", key_default); + key_default = "Escape"; + char* key_exit = (i == 0) ? key_default : config_get(config, "key_exit", key_default); + key_default = "Page_Up"; + char* key_pgup = (i == 0) ? key_default : config_get(config, "key_pgup", key_default); + key_default = "Page_Down"; + char* key_pgdn = (i == 0) ? key_default : config_get(config, "key_pgdn", key_default); + key_default = ""; + char* key_expand = (i == 0) ? key_default: config_get(config, "key_expand", key_default); + key_default = ""; + char* key_hide_search = (i == 0) ? key_default: config_get(config, "key_hide_search", key_default); + key_default = "Ctrl-c"; + char* key_copy = (i == 0) ? key_default : config_get(config, "key_copy", key_default); + + char* keys_custom[CUSTOM_KEY_NUMBER]; + key_default = ""; + for (uint8_t kc_index = 0; kc_index < (sizeof(keys_custom) / sizeof(char*)); kc_index++) { + char config_entry[15]; + sprintf(config_entry, "key_custom_%d", kc_index); + keys_custom[kc_index] = (i == 0) ? key_default : config_get(config, config_entry, key_default); + } + + add_key_entry(key_up, move_up); + add_key_entry(key_down, move_down); + add_key_entry(key_left, move_left); + add_key_entry(key_right, move_right); + add_key_entry(key_forward, move_forward); + add_key_entry(key_backward, move_backward); + add_key_entry(key_submit, NULL); //submit is a special case, when a NULL action is encountered submit is used instead + add_key_entry(key_exit, do_exit); + add_key_entry(key_pgup, move_pgup); + add_key_entry(key_pgdn, move_pgdn); + add_key_entry(key_expand, do_expand); + add_key_entry(key_hide_search, do_hide_search); + add_key_entry(key_copy, do_copy); + + #define CUSTOM_KEY_FUNC(index) do_custom_key_##index + + add_key_entry(keys_custom[0], CUSTOM_KEY_FUNC(0)); + add_key_entry(keys_custom[1], CUSTOM_KEY_FUNC(1)); + add_key_entry(keys_custom[2], CUSTOM_KEY_FUNC(2)); + add_key_entry(keys_custom[3], CUSTOM_KEY_FUNC(3)); + add_key_entry(keys_custom[4], CUSTOM_KEY_FUNC(4)); + add_key_entry(keys_custom[5], CUSTOM_KEY_FUNC(5)); + add_key_entry(keys_custom[6], CUSTOM_KEY_FUNC(6)); + add_key_entry(keys_custom[7], CUSTOM_KEY_FUNC(7)); + add_key_entry(keys_custom[8], CUSTOM_KEY_FUNC(8)); + add_key_entry(keys_custom[9], CUSTOM_KEY_FUNC(9)); + add_key_entry(keys_custom[10], CUSTOM_KEY_FUNC(10)); + add_key_entry(keys_custom[11], CUSTOM_KEY_FUNC(11)); + add_key_entry(keys_custom[12], CUSTOM_KEY_FUNC(12)); + add_key_entry(keys_custom[13], CUSTOM_KEY_FUNC(13)); + add_key_entry(keys_custom[14], CUSTOM_KEY_FUNC(14)); + add_key_entry(keys_custom[15], CUSTOM_KEY_FUNC(15)); + add_key_entry(keys_custom[16], CUSTOM_KEY_FUNC(16)); + add_key_entry(keys_custom[17], CUSTOM_KEY_FUNC(17)); + add_key_entry(keys_custom[18], CUSTOM_KEY_FUNC(18)); + add_key_entry(keys_custom[19], CUSTOM_KEY_FUNC(19)); + } modes = map_init_void(); @@ -1824,8 +2052,7 @@ static char* geo_str[2]; geo_str[0] = width_str; geo_str[1] = height_str; - gdk_threads_add_timeout(50, do_percent_size, geo_str); - do_percent_size(geo_str); + gdk_threads_add_timeout(25, do_percent_size_first, geo_str); } wl_list_init(&mode_list); @@ -1836,4 +2063,6 @@ gtk_window_set_title(GTK_WINDOW(window), prompt); gtk_widget_show_all(window); + + on_exit(on_exit_set_custom_key_return_code, NULL); }