├── .gitignore ├── test_config ├── wofi │ ├── config │ └── style.css └── alacritty │ └── alac_tran.toml ├── scripts ├── Makefile └── keyboard_listener.c ├── mpv_startup.sh ├── LICENSE ├── config ├── config.json ├── wofi_run.json ├── ranger.json └── calcurse.json ├── README.md └── funcher.sh /.gitignore: -------------------------------------------------------------------------------- 1 | Ignore compiled files 2 | keyboard_listener 3 | 4 | *.o 5 | *.mov -------------------------------------------------------------------------------- /test_config/wofi/config: -------------------------------------------------------------------------------- 1 | # allow_images=true 2 | # image_size=24 3 | prompt= 4 | hide_scroll=true 5 | width=30% 6 | height=32% 7 | expander_box=false 8 | -------------------------------------------------------------------------------- /scripts/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for compiling the libinput 2 | 3 | CC = gcc 4 | CFLAGS = -Wall -Wextra -std=c99 5 | # pkg-config is used to get the correct compiler and linker flags 6 | LDFLAGS = $(shell pkg-config --libs libinput libudev) 7 | CFLAGS += $(shell pkg-config --cflags libinput libudev) 8 | 9 | TARGET = keyboard_listener 10 | 11 | .PHONY: all clean 12 | 13 | all: $(TARGET) 14 | 15 | $(TARGET): keyboard_listener.c 16 | $(CC) $(CFLAGS) -o $(TARGET) keyboard_listener.c $(LDFLAGS) 17 | 18 | clean: 19 | rm -f $(TARGET) 20 | -------------------------------------------------------------------------------- /test_config/wofi/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | border-radius:0px; 3 | } 4 | 5 | window { 6 | font-size: 24px; 7 | /* font-family: "Fixedsys Excelsior 3.01", Regular; */ 8 | background-color: rgba(0, 0, 0, 0.0); 9 | border: 0px solid #41EC55; 10 | color: rgba(86, 231, 151, 0.7); 11 | } 12 | 13 | #entry{ 14 | padding: 0.22em; 15 | min-height: 28px; 16 | } 17 | 18 | #entry:selected { 19 | color: #41EC55; 20 | background-color: rgba(86, 231, 151, 0.15); 21 | } 22 | 23 | #expander-box { 24 | background-color: rgba(14, 21, 14, 0.85); 25 | color: #41EC55; 26 | } 27 | 28 | 29 | #text:selected { 30 | color: #41EC55; 31 | } 32 | 33 | 34 | #input{ 35 | background-color: rgba(0, 0, 0, 0.0); 36 | color: #41EC55; 37 | border: 2px solid rgba(86, 231, 151, 0.25); 38 | padding: 0.22em; 39 | } 40 | 41 | #input:focus { 42 | box-shadow: 0px 0px 0px 0px #41EC55 inset; 43 | 44 | } 45 | 46 | #input image { 47 | color: #41EC55; 48 | } 49 | 50 | image{ 51 | margin-left: 0.25em; 52 | margin-right: 0.25em; 53 | } 54 | -------------------------------------------------------------------------------- /mpv_startup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mpv_control="--no-config --profile=fast --title=mpv_preload \ 4 | --hwdec=auto-copy \ 5 | --vo=gpu \ 6 | --cache=yes --demuxer-max-bytes=100M \ 7 | --demuxer-readahead-secs=3 \ 8 | --scale=bilinear \ 9 | --cscale=bilinear \ 10 | --dscale=bilinear \ 11 | --vd-lavc-skiploopfilter=all \ 12 | --deband=no \ 13 | --no-border \ 14 | --background=none \ 15 | --no-osc \ 16 | --no-input-default-bindings \ 17 | --input-conf=/dev/null \ 18 | --no-terminal \ 19 | --idle \ 20 | --pause \ 21 | --force-window=no \ 22 | --keep-open=always" 23 | 24 | socket="/tmp/mpv_socket" 25 | 26 | # cleanup old instances 27 | pkill -f "mpv.*mpv_preload" 2>/dev/null 28 | rm -f /tmp/mpv_socket* 29 | 30 | # preload 31 | setsid mpv --input-ipc-server="$socket" $mpv_control & 32 | pid=$! 33 | 34 | # wait for socket 35 | for i in {1..50}; do 36 | [ -S "$socket" ] && break 37 | sleep 0.1 38 | done 39 | 40 | if [ ! -S "$socket" ]; then 41 | echo "Failed to start mpv preload." 42 | kill "$pid" 2>/dev/null 43 | exit 1 44 | fi 45 | 46 | echo "mpv preload started (pid $pid, socket $socket)" 47 | 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Tong-ST 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": ["Currently tested on i3/x11 and sway/wayland, You can try on hyprland as well but not tested"], 3 | "CURRENT_WM": "sway", 4 | "VIDEO_PATH": "~/Funcher/assets/pip_1080p.mov", 5 | 6 | "_comment": ["This is main Video loop must include all timestamp"], 7 | "_comment": ["Time format of VDO now MIN:SEC;FRAME For example 1:12;30 would be 1 minute 12 second in frame 30"], 8 | "VIDEO_FPS": "60", 9 | "VIDEO_CTRL": { 10 | "START": "0;15", 11 | "IDLE_LOOP": ["2;50", "8;50"], 12 | "EXIT": ["11;00", "11;30"] 13 | }, 14 | 15 | "_comment": ["Main Launcher config use it like using in terminal"], 16 | "LAUNCHER": { 17 | "APP_CLASS": "Wofi", 18 | "APP_ID": "wofi", 19 | "RUN_ARG": "wofi --show drun --normal-window", 20 | "DELAY_START": "0.4", 21 | "OFFSET_XY": ["80", "-120"], 22 | "WIDTH_HEIGHT": ["600", "400"] 23 | }, 24 | 25 | "OTHERS": { 26 | "_comment": ["These experimental for hyprland use workspace instead of scratchpad to show/hide app"], 27 | "CUSTOM_WORKSPACE": "21" 28 | }, 29 | 30 | "_comment": ["(OPTIONAL) Input base player to get input key use -k, Don't add exit app key like esc/enter it might overlap with VIDEO_CTRL > EXIT"], 31 | "_comment": ["input_key: [start_timestamp, end_timestamp, hide>start] = Hide APP while playing this key then play START, Also have hide>idle "], 32 | "INPUT": { 33 | "103": ["12;30", "12;48"], 34 | "up": ["12;30", "12;48"], 35 | "108": ["12;00", "12;18"], 36 | "down": ["12;00", "12;18"], 37 | "15": ["13;12", "13;34"], 38 | "middle": ["9;00", "10;00", "hide>start"] 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /config/wofi_run.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": ["Currently tested on i3/x11 and sway/wayland, You can try on hyprland as well but not tested"], 3 | "CURRENT_WM": "sway", 4 | "VIDEO_PATH": "~/Funcher/assets/pip_1080p.mov", 5 | 6 | "_comment": ["This is main Video loop must include all timestamp"], 7 | "_comment": ["Time format of VDO now MIN:SEC;FRAME For example 1:12;30 would be 1 minute 12 second in frame 30"], 8 | "VIDEO_FPS": "60", 9 | "VIDEO_CTRL": { 10 | "START": "0;15", 11 | "IDLE_LOOP": ["2;50", "8;50"], 12 | "EXIT": ["11;00", "11;30"] 13 | }, 14 | 15 | "_comment": ["Main Launcher config use it like using in terminal"], 16 | "LAUNCHER": { 17 | "APP_CLASS": "Wofi", 18 | "APP_ID": "wofi", 19 | "RUN_ARG": "wofi --show run --normal-window", 20 | "DELAY_START": "0.4", 21 | "OFFSET_XY": ["-130", "-270"], 22 | "WIDTH_HEIGHT": ["600", "400"] 23 | }, 24 | 25 | "OTHERS": { 26 | "_comment": ["These experimental for hyprland use workspace instead of scratchpad to show/hide app"], 27 | "CUSTOM_WORKSPACE": "21" 28 | }, 29 | 30 | "_comment": ["(OPTIONAL) Input base player to get input key use -k, Don't add exit app key like esc/enter it might overlap with VIDEO_CTRL > EXIT"], 31 | "_comment": ["input_key: [start_timestamp, end_timestamp, hide>start] = Hide APP while playing this key then play START, Also have hide>idle "], 32 | "INPUT": { 33 | "103": ["12;30", "12;48"], 34 | "up": ["12;30", "12;48"], 35 | "108": ["12;00", "12;18"], 36 | "down": ["12;00", "12;18"], 37 | "15": ["13;12", "13;34"], 38 | "middle": ["9;00", "10;00", "hide>start"] 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /config/ranger.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": ["Currently tested on i3/x11 and sway/wayland, You can try on hyprland as well but not tested"], 3 | "CURRENT_WM": "sway", 4 | "VIDEO_PATH": "~/Funcher/assets/pip_1080p.mov", 5 | 6 | "_comment": ["This is main Video loop must include all timestamp"], 7 | "_comment": ["Time format of VDO now MIN:SEC;FRAME For example 1:12;30 would be 1 minute 12 second in frame 30"], 8 | "VIDEO_FPS": "60", 9 | "VIDEO_CTRL": { 10 | "START": "0;15", 11 | "IDLE_LOOP": ["2;50", "8;50"], 12 | "EXIT": ["11;00", "11;30"] 13 | }, 14 | 15 | "_comment": ["Main Launcher config use it like using in terminal"], 16 | "LAUNCHER": { 17 | "APP_CLASS": "rangerTerm", 18 | "APP_ID": "rangerTerm", 19 | "RUN_ARG": "alacritty --config-file $HOME/.config/alacritty/alac_tran.toml --class rangerTerm -e ranger", 20 | "DELAY_START": "0.4", 21 | "OFFSET_XY": ["80", "-120"], 22 | "WIDTH_HEIGHT": ["600", "400"] 23 | }, 24 | 25 | "OTHERS": { 26 | "_comment": ["These experimental for hyprland use workspace instead of scratchpad to show/hide app"], 27 | "CUSTOM_WORKSPACE": "21" 28 | }, 29 | 30 | "_comment": ["(OPTIONAL) Input base player to get input key use -k, Don't add exit app key like esc/enter it might overlap with VIDEO_CTRL > EXIT"], 31 | "_comment": ["input_key: [start_timestamp, end_timestamp, hide>start] = Hide APP while playing this key then play START, Also have hide>idle "], 32 | "INPUT": { 33 | "103": ["12;30", "12;48"], 34 | "up": ["12;30", "12;48"], 35 | "108": ["12;00", "12;18"], 36 | "down": ["12;00", "12;18"], 37 | "15": ["13;12", "13;34"], 38 | "middle": ["9;00", "10;00", "hide>start"] 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /config/calcurse.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": ["Currently tested on i3/x11 and sway/wayland, You can try on hyprland as well but not tested"], 3 | "CURRENT_WM": "sway", 4 | "VIDEO_PATH": "~/Funcher/assets/pip_1080p.mov", 5 | 6 | "_comment": ["This is main Video loop must include all timestamp"], 7 | "_comment": ["Time format of VDO now MIN:SEC;FRAME For example 1:12;30 would be 1 minute 12 second in frame 30"], 8 | "VIDEO_FPS": "60", 9 | "VIDEO_CTRL": { 10 | "START": "0;15", 11 | "IDLE_LOOP": ["2;50", "8;50"], 12 | "EXIT": ["11;00", "11;30"] 13 | }, 14 | 15 | "_comment": ["Main Launcher config use it like using in terminal"], 16 | "LAUNCHER": { 17 | "APP_CLASS": "calcurseTerm", 18 | "APP_ID": "calcurseTerm", 19 | "RUN_ARG": "alacritty --config-file $HOME/.config/alacritty/alac_tran.toml --class calcurseTerm -e calcurse", 20 | "DELAY_START": "0.4", 21 | "OFFSET_XY": ["80", "-120"], 22 | "WIDTH_HEIGHT": ["600", "400"] 23 | 24 | }, 25 | 26 | "OTHERS": { 27 | "_comment": ["These experimental for hyprland use workspace instead of scratchpad to show/hide app"], 28 | "CUSTOM_WORKSPACE": "21" 29 | }, 30 | 31 | "_comment": ["(OPTIONAL) Input base player to get input key use -k, Don't add exit app key like esc/enter it might overlap with VIDEO_CTRL > EXIT"], 32 | "_comment": ["input_key: [start_timestamp, end_timestamp, hide>start] = Hide APP while playing this key then play START, Also have hide>idle "], 33 | "INPUT": { 34 | "103": ["12;30", "12;48"], 35 | "up": ["12;30", "12;48"], 36 | "108": ["12;00", "12;18"], 37 | "down": ["12;00", "12;18"], 38 | "15": ["13;12", "13;34"], 39 | "middle": ["9;00", "10;00", "hide>start"] 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /test_config/alacritty/alac_tran.toml: -------------------------------------------------------------------------------- 1 | # Window setting 2 | [window] 3 | padding = { x = 5, y = 5 } # Padding around the terminal content 4 | opacity = 0.0 # Window opacity (0.0 - 1.0) 5 | decorations = "full" # Window decorations (full, transparent, none) 6 | 7 | # Font settings 8 | [font] 9 | size = 16.0 10 | #[font.normal] 11 | #family = "Fixedsys Excelsior 3.01" 12 | #style = "Regular" 13 | #[font.bold] 14 | #family = "Fixedsys Excelsior 3.01" 15 | #style = "Bold" 16 | #[font.italic] 17 | #family = "Fixedsys Excelsior 3.01" 18 | #style = "Italic" 19 | #[font.bold_italic] 20 | #family = "Fixedsys Excelsior 3.01" 21 | #style = "Bold Italic" 22 | 23 | # Color scheme 24 | [colors] 25 | draw_bold_text_with_bright_colors = true 26 | [colors.primary] 27 | background = "#0E150E" 28 | foreground = "#BFF6BF" 29 | [colors.normal] 30 | black = '#000000' # Black 31 | red = '#ff5555' # Bright Red 32 | green = '#41EC55' # Bright Green 33 | yellow = '#f1fa8c' # Bright Yellow 34 | blue = '#bd93f9' # Bright Blue 35 | magenta = '#ff79c6' # Bright Magenta 36 | cyan = '#8be9fd' # Bright Cyan 37 | white = '#f8f8f2' # Bright White 38 | 39 | [colors.bright] 40 | black = '#6272a4' # Bright Black (Gray) 41 | red = '#ff5555' # Bright Red 42 | green = '#41EC55' # Bright Green 43 | yellow = '#f1fa8c' # Bright Yellow 44 | blue = '#bd93f9' # Bright Blue 45 | magenta = '#ff79c6' # Bright Magenta 46 | cyan = '#8be9fd' # Bright Cyan 47 | white = '#ffffff' # Bright White 48 | 49 | # Scrolling settings 50 | [scrolling] 51 | history = 10000 # Maximum number of lines in scrollback buffer 52 | multiplier = 3 # Number of lines scrolled per increment 53 | 54 | # Key bindings 55 | [keyboard] 56 | bindings = [ 57 | { key = "Return", mods = "Control|Shift", action = "SpawnNewInstance" }, # Spawn new instance 58 | { key = "Space", mods = "Control|Shift", action = "ToggleViMode" }, # Toggle Vi mode 59 | ] 60 | -------------------------------------------------------------------------------- /scripts/keyboard_listener.c: -------------------------------------------------------------------------------- 1 | #define _DEFAULT_SOURCE // Required for usleep 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // This is a required callback function for libinput. 13 | // It opens a device file path. 14 | static int open_restricted(const char *path, int flags, void *user_data) { 15 | (void)user_data; // Suppress unused parameter warning 16 | int fd = open(path, flags); 17 | if (fd < 0) { 18 | fprintf(stderr, "Failed to open %s (%s)\n", path, strerror(errno)); 19 | } 20 | return fd < 0 ? -errno : fd; 21 | } 22 | 23 | // This is another required callback function. 24 | // It simply closes a file descriptor. 25 | static void close_restricted(int fd, void *user_data) { 26 | (void)user_data; // Suppress unused parameter warning 27 | close(fd); 28 | } 29 | 30 | // The interface struct with our callback functions. 31 | static const struct libinput_interface interface = { 32 | .open_restricted = open_restricted, 33 | .close_restricted = close_restricted, 34 | }; 35 | 36 | int main(void) { 37 | struct libinput *li; 38 | struct libinput_event *event; 39 | struct udev *udev = udev_new(); 40 | 41 | if (!udev) { 42 | fprintf(stderr, "Failed to initialize udev\n"); 43 | return 1; 44 | } 45 | printf("udev context created successfully.\n"); 46 | 47 | li = libinput_udev_create_context(&interface, NULL, udev); 48 | if (!li) { 49 | fprintf(stderr, "Failed to initialize libinput from udev\n"); 50 | udev_unref(udev); 51 | return 1; 52 | } 53 | printf("libinput context created successfully.\n"); 54 | 55 | if (libinput_udev_assign_seat(li, "seat0") != 0) { 56 | fprintf(stderr, "Failed to assign seat 'seat0'.\n"); 57 | libinput_unref(li); 58 | udev_unref(udev); 59 | return 1; 60 | } 61 | printf("Assigned to seat0 successfully.\n"); 62 | 63 | 64 | printf("\nListening for events... Press any key. Press Ctrl+C to exit.\n"); 65 | fflush(stdout); 66 | 67 | // Main event loop 68 | while (1) { 69 | libinput_dispatch(li); // Process available events 70 | event = libinput_get_event(li); 71 | 72 | if (!event) { 73 | usleep(1000); 74 | continue; 75 | } 76 | 77 | enum libinput_event_type type = libinput_event_get_type(event); 78 | 79 | if (type == LIBINPUT_EVENT_DEVICE_ADDED) { 80 | struct libinput_device *device = libinput_event_get_device(event); 81 | printf("--- DEVICE ADDED ---\n"); 82 | printf(" Name: %s\n", libinput_device_get_name(device)); 83 | if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD)) { 84 | printf(" This device IS a keyboard.\n"); 85 | } else { 86 | printf(" This device is NOT a keyboard.\n"); 87 | } 88 | if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) { 89 | printf(" This device IS a mouse/pointer.\n"); 90 | } 91 | printf("--------------------\n"); 92 | } 93 | 94 | 95 | if (type == LIBINPUT_EVENT_KEYBOARD_KEY) { 96 | struct libinput_event_keyboard *key_event = libinput_event_get_keyboard_event(event); 97 | enum libinput_key_state key_state = libinput_event_keyboard_get_key_state(key_event); 98 | uint32_t key_code = libinput_event_keyboard_get_key(key_event); 99 | 100 | if (key_state == LIBINPUT_KEY_STATE_PRESSED) { 101 | printf("%u\n", key_code); 102 | 103 | // --- NEW EFFICIENT METHOD --- 104 | // 1. Create a buffer to hold the command string. 105 | char command[256]; 106 | 107 | // 2. Format the command into the buffer using snprintf. 108 | snprintf(command, sizeof(command), "echo %u > /tmp/input_key.txt", key_code); 109 | 110 | // 3. Execute the fully formed command. 111 | int return_value = system(command); 112 | if (return_value != 0) { 113 | fprintf(stderr, "Command execution failed!\n"); 114 | } 115 | } 116 | } 117 | if (type == LIBINPUT_EVENT_POINTER_AXIS) { 118 | struct libinput_event_pointer *pointer_event = libinput_event_get_pointer_event(event); 119 | 120 | // Check if the event is for the vertical scroll axis 121 | if (libinput_event_pointer_has_axis(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) { 122 | double scroll_value = libinput_event_pointer_get_axis_value(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); 123 | const char* direction = "unknown"; 124 | 125 | if (scroll_value < 0) { 126 | direction = "up"; 127 | printf("%s\n", direction); 128 | } else if (scroll_value > 0) { 129 | direction = "down"; 130 | printf("%s\n", direction); 131 | } 132 | 133 | char command[256]; 134 | snprintf(command, sizeof(command), "echo %s > /tmp/input_key.txt", direction); 135 | system(command); 136 | } 137 | } 138 | 139 | if (type == LIBINPUT_EVENT_POINTER_BUTTON) { 140 | struct libinput_event_pointer *pointer_event = libinput_event_get_pointer_event(event); 141 | enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event); 142 | 143 | if (button_state == LIBINPUT_BUTTON_STATE_PRESSED) { 144 | uint32_t button_code = libinput_event_pointer_get_button(pointer_event); 145 | const char* button_name = "unknown"; 146 | 147 | switch (button_code) { 148 | case BTN_LEFT: 149 | button_name = "left"; 150 | printf("%s\n", button_name); 151 | break; 152 | case BTN_RIGHT: 153 | button_name = "right"; 154 | printf("%s\n", button_name); 155 | break; 156 | case BTN_MIDDLE: 157 | button_name = "middle"; 158 | printf("%s\n", button_name); 159 | break; 160 | } 161 | 162 | char command[256]; 163 | snprintf(command, sizeof(command), "echo %s > /tmp/input_key.txt", button_name); 164 | system(command); 165 | } 166 | } 167 | libinput_event_destroy(event); 168 | fflush(stdout); // Flush after every event for immediate feedback 169 | } 170 | 171 | // Clean up 172 | libinput_unref(li); 173 | udev_unref(udev); 174 | 175 | return 0; 176 | } 177 | 178 | 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FUNCHER - App launcher that also FUN! 2 | 3 | What it does, It just simple script that call [mpv](https://mpv.io) media playback to play custom video and run in background.. 4 | 5 | So my concept here to make it work with others app launcher that already great like `wofi` or you can set to run your others fav app as well 6 | 7 | 8 | 9 | ## Current Stage 10 | NOW SUPPORT x11, for i3wm go to [Funcher/x11](https://github.com/Tong-ST/Funcher/tree/Funcher/x11) branch 11 | 12 | ***Git history rewritten, who clone before 27 sep 2025, Please re-clone for update*** 13 | 14 | This is very first prototype build that just using `Shell script` as main program, also with help of `libinput` code in `C` that track user input and play different section of vdo 15 | 16 | As of current build only work well on `Wayland` i build on sway/debian 13, try to expand to hyprland but not tested yet.. 17 | if you know how to deal with hyprland just look into main script and change WM related command for your setup and maybe share with others 18 | 19 | So, it's first prototype release expect some thing to break 20 | 21 | ## Before install 22 | 23 | Make sure your system have all dependencies needed 24 | 25 | Part A: Main Application Dependencies 26 | The main funcher.sh requires the following command-line tools to be installed: 27 | - mpv: For video playback 28 | - bc: For basic calculations 29 | - jq: For JSON processing 30 | - socat: For IPC/socket communication 31 | 32 | Apps You can use others app but here just quick example to get you started 33 | - wofi: For application launching / menus (works on Wayland) 34 | - alacritty: For run terminal base app 35 | 36 | Installation Instructions: 37 | 38 | - For Debian / Ubuntu / Mint: 39 | ``` 40 | sudo apt-get update 41 | sudo apt-get install mpv bc jq socat wofi alacritty 42 | ``` 43 | 44 | - For Fedora / CentOS / RHEL: 45 | ``` 46 | sudo dnf install mpv bc jq socat wofi alacritty 47 | ``` 48 | 49 | - For Arch Linux: 50 | ``` 51 | sudo pacman -S mpv bc jq socat wofi alacritty 52 | ``` 53 | 54 | Part B: Input Listener Compilation Dependencies 55 | - libinput 56 | - libudev 57 | 58 | Installation Instructions: 59 | 60 | - For Debian / Ubuntu / Mint: 61 | ``` 62 | sudo apt-get install build-essential libinput-dev libudev-dev 63 | ``` 64 | 65 | - For Fedora / CentOS / RHEL: 66 | ``` 67 | sudo dnf groupinstall "Development Tools" 68 | sudo dnf install libinput-devel systemd-devel 69 | ``` 70 | (Note: libudev development files are included in systemd-devel on these systems) 71 | 72 | - For Arch Linux: 73 | ``` 74 | sudo pacman -S base-devel libinput 75 | ``` 76 | 77 | ## Installation & Setup 78 | 79 | For ready to use, I install in $HOME directory if you want to changes, You may need to adjust path in config files 80 | 81 | 1. **Git clone & Build** 82 | ``` 83 | cd $HOME 84 | git clone https://github.com/Tong-ST/Funcher.git 85 | cd Funcher 86 | chmod +x funcher.sh mpv_startup.sh 87 | cd scripts 88 | make keyboard_listener 89 | ``` 90 | 91 | 2. **Setup Config** 92 | - In [Releases](https://github.com/Tong-ST/Funcher/releases/) you'll see pip-boy-vdo.tar.xz grab and put it on Funcher/assets 93 | 94 | ``` 95 | tar xfv pip-boy-vdo.tar.xz 96 | mkdir $HOME/Funcher/assets/ 97 | cp pip_1080p.mov $HOME/Funcher/assets/ 98 | ``` 99 | - Setup your config files This setup is for `sway` only others wayland you may need different config that do the same thing 100 | 101 | - In your sway .config you should look like these 102 | 103 | ``` 104 | ### mpv setup 105 | for_window [title="mpv_preload"] floating enable, border none, resize set 1920 1080 106 | 107 | ### Your app setup 108 | for_window [app_id="wofi"] floating enable, border none 109 | for_window [app_id="calcurseTerm"] floating enable, border none 110 | for_window [app_id="rangerTerm"] floating enable, border none 111 | 112 | ### Funcher Key-Binding 113 | bindsym $mod+d exec $HOME/Funcher/funcher.sh 114 | bindsym $mod+shift+d exec $HOME/Funcher/funcher.sh -c $HOME/Funcher/config/wofi_run.json 115 | 116 | ### Others app to try, Before using you need to install in your system first in debian ` sudo apt install ranger calcurse ` Uncomment to try out 117 | # bindsym $mod+shift+x exec $HOME/Funcher/funcher.sh -c $HOME/Funcher/config/calcurse.json 118 | # bindsym $mod+shift+t exec $HOME/Funcher/funcher.sh -c $HOME/Funcher/config/ranger.json 119 | ``` 120 | These are example config and it should be self explain and look into Funcher/config/ as well 121 | 122 | **NOTED:** If you having problem with app flickering at launch, Because in current script, Window offset/size will change after app launched, So it appear at normal size for a sec then will change offset/size respect to your Funcher/config 123 | - If you want to fix that you can do like in sway .config ` for_window [app_id="..."] move scratchpad ` This will set this app_id to spawn in scratchpad than it will appear as you offset/size in Funcher/config also you need to change new offset position in Funcher/config as well 124 | - I didn't default it because it might interfere with your others app that share same ID and if you run on very fast CPU it won't be notice 125 | - Or if you only use on 1 monitor you can just set fix spawn position in sway .config ` for_window [app_id="..."] move position , resize set ` and leave Funcher/config offset/size empty 126 | 127 | 3. **Set up others app config** like wofi, alacritty 128 | ``` 129 | cp -r $HOME/Funcher/test_config/wofi $HOME/.config/ 130 | cp -r $HOME/Funcher/test_config/alacritty $HOME/.config/ 131 | ``` 132 | 4. **Add input to user group** for input base video you just need to do it one time 133 | ``` 134 | sudo usermod -aG input $USER 135 | ``` 136 | Than logout and login back see that now mpv change video segment base on your input 137 | 138 | - In some cases you might need to make funcher.sh executable ` chmod +x funcher.sh ` But most case clone from git don't need this 139 | 140 | ## Usage 141 | For normal use case just set keybinding for each app point those config file like you see in sway .config example 142 | - **Or to test/debug** in Funcher directory use for example 143 | - ./funcher.sh -c /config/config.json # To run main conifg with wofi 144 | - ./funcher.sh -k # To get input keycode to use on custom VDO segment 145 | 146 | - **Know your config** for each app should have they own .json file in Funcher/config folder you'll see you can set your own VDO, Input, What WMs you using 147 | - **IT'S IMPORTANT** to check in .json file like in vdo path make sure is correct, Your **CURRENT_WM : sway or hyprland** in config file comment should be explain the concept of each 148 | 149 | 150 | ## Limitation 151 | - mpv, So this app is just command mpv playing in background It not a lightweight build yet as i tested if you got wrong Video codec that not support ` Hardware Acceleration ` It going to tank your CPU quite a lot 152 | - As am i develop I found out that the reliable VDO format that play in mpv natively with transparency background right now ` .mov ` is the way to go The file are quite big but it work, I also try like .webm that really small size but can't get trans bg to work with mpv, So if i found the better way in the future will be updated 153 | - Also if we can reduce VDO size and able to keep basic need like BG transparency, HW accel, Quality Please let me know, Right now just have to trade-off with bigger VDO files but it's no lag using it realtime 154 | 155 | ## Contribution 156 | In this project I consider 2 main roles of contribute to community 157 | 1. **Creator** - This project is mainly on creativity side, For example VDO editing as you can see, it's just pure video editing skill, you can craft your own VIDEO what ever you want, No coding experience are required, just tinker with config file should be enough 158 | - Some tips I've learn, For new video edit, If one time animation like click down do gear spin, I use base clip that about to go idle loop, And edit edit from that so when this anim done it trasit smoothly to idle 159 | - Background remover mostly use chroma-key, But if background are not separate, You might need to hand cut with Rotoscoping or use object-detection 160 | 161 | 2. **Developer** - This very early build is just shell script, I think there are a ton of room to improve like moving to others language like `C` or `lua` I don't know yet in C maybe you can communicate seamless with input listener, or lua maybe it can improve more playback performance with mpv 162 | also expand to different WMs / Distro as possible 163 | 164 | Have to admit i really new to programming, As well as VDO editing just know about chroma-key in this project :) 165 | 166 | So, If you guy interested to contribute in this project i think you already has better skill than me really. 167 | 168 | > To contribute send me an email goodywolf101@gmail.com 169 | 170 | Any recommend would be golden! 171 | 172 | ## Goal 173 | In this very first stage i just want to expand to be reliable on others window manager as soon as possible, currently on Sway/Wayland and i3/x11 174 | 175 | ## Support 176 | - If you like this project consider support at [Ko-fi](https://www.ko-fi.com/goodywolf) a cup of coffees it's already whole days for me in Thailand :) haha 177 | 178 | Buy Me a Coffee at ko-fi.com 179 | 180 | ## Thanks & Credit 181 | - [mpv media player](https://mpv.io) is heart of this project 182 | - [libinput](https://wiki.archlinux.org/title/Libinput) For easy to integrate input listening tool 183 | - [bucklespring](https://github.com/zevv/bucklespring) For the amazing keyboard sound effect, Also inspiration for input listening base video playback 184 | - [my own Sway .config](https://www.github.com/Tong-ST/pip-boy-sway) In case you want to see my whole setup 185 | 186 | Special thanks to creator of Fallout mods 187 | - [Fallout animation mod](https://www.nexusmods.com/newvegas/mods/91200) 188 | - [Pipboy skin mod](https://www.nexusmods.com/newvegas/mods/91369) 189 | -------------------------------------------------------------------------------- /funcher.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Program Requirement (debian base) others may has different packages name 4 | # DEP: socat, bc, jq, build-essential libinput-dev libudev-dev 5 | # APP: mpv, wofi or others app you want to try.. 6 | # DE: Currently on sway/wayland and i3/x11, in hyprland not tested yet but been added some potential support 7 | # For input base anim need to add input group in debian ` sudo usermod -aG input $USER ` 8 | 9 | # Build Check 10 | REQUIRED_COMMANDS=("mpv" "bc" "jq" "socat") 11 | 12 | for cmd in "${REQUIRED_COMMANDS[@]}"; do 13 | if ! command -v "$cmd" &> /dev/null; then 14 | echo "Error: Required command '$cmd' is not installed." 15 | echo "Please install it and try again." 16 | exit 1 17 | fi 18 | done 19 | echo "Build checked: pass" 20 | 21 | SOCK="/tmp/mpv_socket" 22 | PIPE="/tmp/input_key.txt" 23 | LOCKFILE="/tmp/anim_cooldown.lock" 24 | EXIT_STATE="/tmp/funcer_exit_$$" 25 | LAST_KEY="/tmp/last_key.txt" 26 | MAIN_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) 27 | KEY_LISTEN="$MAIN_DIR/scripts/keyboard_listener" 28 | MPV_STARTUP="$MAIN_DIR/mpv_startup.sh" 29 | 30 | READER_PID="" 31 | INPUT_PID="" 32 | LAUNCHER_PID="" 33 | WINDOW_PID="" 34 | 35 | # CONFIG FILE 36 | CONFIG=$MAIN_DIR/config/config.json 37 | 38 | # Options 39 | RUN_KEY_LISTEN=0 40 | while getopts "c:k" opt; do 41 | case $opt in 42 | c) CONFIG="$OPTARG" ;; 43 | k) RUN_KEY_LISTEN=1 ;; 44 | 45 | *) echo "Usage: $0