diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..1dfc776 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: wvffle +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ['https://paypal.me/wvffle'] diff --git a/.gitignore b/.gitignore index 37be74a..577f287 100644 --- a/.gitignore +++ b/.gitignore @@ -1,71 +1,3 @@ -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser +/target +**/*.rs.bk +.idea/ \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..5c98b42 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 8822db8..28a804d 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,5 @@ - diff --git a/.idea/modules.xml b/.idea/modules.xml index 116be26..8f9b256 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/waffy-rs.iml b/.idea/waffy-rs.iml new file mode 100644 index 0000000..b7b4242 --- /dev/null +++ b/.idea/waffy-rs.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/waffy.iml b/.idea/waffy.iml deleted file mode 100644 index f08604b..0000000 --- a/.idea/waffy.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 5d5fc7f..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project(waffy C) - -set(CMAKE_C_STANDARD 11) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "$ENV{HOME}/.local/share/${PROJECT_NAME}") - -file(COPY assets DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") - -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK3 REQUIRED gtk+-wayland-3.0) -pkg_check_modules(GTK3_LAYER_SHELL REQUIRED gtk-layer-shell-0) - -pkg_search_module(WAYLAND wayland-client) -pkg_search_module(WAYLAND_SCANNER wayland-scanner) -pkg_search_module(WAYLAND_PROTOCOLS wayland-protocols) - - -include_directories(${GTK3_INCLUDE_DIRS}) -link_directories(${GTK3_LIBRARY_DIRS}) - -#include_directories(${WAYLAND_INCLUDE_DIR}) -#link_directories(${WAYLAND_LIBRARIES}) - -add_definitions(${GTK3_CFLAGS_OTHER}) -#add_definitions(${WAYLAND_DEFINITIONS}) - -add_executable(waffy src/main.c src/utils.h src/utils.c src/find_desktop.c src/find_desktop.h src/desktop_entry.c src/desktop_entry.h src/filter.c src/filter.h src/config.c src/config.h src/colors.c src/colors.h) -target_link_libraries(waffy ${GTK3_LAYER_SHELL_LIBRARIES} ${WAYLAND_LIBRARIES} ${GTK3_LIBRARIES}) diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..38fb9e9 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1031 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "anyhow" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" + +[[package]] +name = "arrayref" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + +[[package]] +name = "atk" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/atk#b56689dea48ac579b53041d89544ff18a72ec08b" +dependencies = [ + "atk-sys", + "bitflags", + "glib", + "glib-sys", + "gobject-sys", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "backtrace" +version = "0.3.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" +dependencies = [ + "backtrace-sys", + "cfg-if", + "libc", + "rustc-demangle", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" + +[[package]] +name = "cairo-rs" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/cairo#b555469778becf3ae547dbf08f2ddc8ffd1c0617" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "glib-sys", + "gobject-sys", + "libc", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.9.2" +source = "git+https://github.com/gtk-rs/cairo#b555469778becf3ae547dbf08f2ddc8ffd1c0617" +dependencies = [ + "glib-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "cc" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" + +[[package]] +name = "crossbeam-utils" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +dependencies = [ + "cfg-if", + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" + +[[package]] +name = "failure" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "futures-channel" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866" + +[[package]] +name = "futures-executor" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e274736563f686a837a0568b478bdabfeaec2dca794b5649b04e2fe1627c231" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e676577d229e70952ab25f3945795ba5b16d63ca794ca9d2c860e5595d20b5ff" + +[[package]] +name = "futures-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e7c56c15537adb4f76d0b7a76ad131cb4d2f4f32d3b0bcabcbe1c7c5e87764" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9" + +[[package]] +name = "futures-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "gdk" +version = "0.12.0" +source = "git+https://github.com/gtk-rs/gdk#63d16649441dba756936c993761d6917c077e10c" +dependencies = [ + "bitflags", + "cairo-rs", + "cairo-sys-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/gdk-pixbuf#060b57509cc106904ce1d9ff142bbfcde224a463" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "gdk-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "gio" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/gio#06558414d906c77c540d8b3caf44a8409f9730f8" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", +] + +[[package]] +name = "gio-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "glib" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/glib#cb01dd6886337d29270c682aeb5814c59e123db8" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", +] + +[[package]] +name = "glib-macros" +version = "0.9.0" +source = "git+https://github.com/gtk-rs/glib#cb01dd6886337d29270c682aeb5814c59e123db8" +dependencies = [ + "anyhow", + "heck", + "itertools", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "gobject-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "glib-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "gtk" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/gtk#f3201e9561f9b4ecaa724bdba4459ad7edc68533" +dependencies = [ + "atk", + "bitflags", + "cairo-rs", + "cairo-sys-rs", + "cc", + "gdk", + "gdk-pixbuf", + "gdk-pixbuf-sys", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk-sys", + "libc", + "once_cell", + "pango", + "pango-sys", +] + +[[package]] +name = "gtk-layer-shell-rs" +version = "0.1.0" +source = "git+https://github.com/subgraph/gtk-layer-shell-rs#6cca35d8d7957cdfaca5431855877ef2d5bef8e3" +dependencies = [ + "bitflags", + "gdk", + "gio", + "glib", + "glib-sys", + "gtk", + "gtk-layer-shell-sys", + "gtk-sys", + "libc", +] + +[[package]] +name = "gtk-layer-shell-sys" +version = "0.0.1" +source = "git+https://github.com/subgraph/gtk-layer-shell-rs#6cca35d8d7957cdfaca5431855877ef2d5bef8e3" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.9.2" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +dependencies = [ + "either", +] + +[[package]] +name = "json5" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb48cdfbe18a1ef5ce0a0edc30b8b8f61422f7073f709dd09311c2b3d2bba6" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "once_cell" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "pango" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/pango#377c7d1e4a9a58749be9cb81df71b84ac65a05e1" +dependencies = [ + "bitflags", + "glib", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "pest" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df43fd99896fd72c485fe47542c7b500e4ac1e8700bf995544d1317a60ded547" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + +[[package]] +name = "pin-utils" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" + +[[package]] +name = "proc-macro-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c98547ceaea14eeb26fcadf51dc70d01a2479a7839170eae133721105e4428" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "proc-macro-error-attr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2bf5d493cf5d3e296beccfd61794e445e830dfc8070a9c248ad3ee071392c6c" +dependencies = [ + "proc-macro2", + "quote", + "rustversion", + "syn", + "syn-mid", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro-nested" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" + +[[package]] +name = "proc-macro2" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "redox_users" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d" +dependencies = [ + "failure", + "rand_os", + "redox_syscall", + "rust-argon2", +] + +[[package]] +name = "rust-argon2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" +dependencies = [ + "base64", + "blake2b_simd", + "crossbeam-utils", +] + +[[package]] +name = "rust-embed" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b18893bdbdb0fa5bce588f5d7ab4afbd0678fc879d31535912bf39b7fbc062d6" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50633968284cfc373661345fc6382e62b738079f045738023ebc5e445cf44357" +dependencies = [ + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97655158074ccb2d2cfb1ccb4c956ef0f4054e43a2c1e71146d4991e6961e105" +dependencies = [ + "walkdir", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" + +[[package]] +name = "rustversion" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a0538bd897e17257b0128d2fd95c2ed6df939374073a36166051a79e2eb7986" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "same-file" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "sublime_fuzzy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdac3d983d073c19487ba1f5e16eda43e9c6e50aa895d87110d0febe389b66b9" + +[[package]] +name = "syn" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "syn-mid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" + +[[package]] +name = "ucd-trie" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2" + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "waffy" +version = "0.1.0" +dependencies = [ + "dirs", + "gdk", + "gdk-pixbuf", + "gio", + "gtk", + "gtk-layer-shell-rs", + "json5", + "once_cell", + "pango", + "rust-embed", + "serde", + "sublime_fuzzy", +] + +[[package]] +name = "walkdir" +version = "2.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5e818fa --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "waffy" +version = "0.1.0" +authors = ["Kasper Seweryn "] +edition = "2018" + +[dependencies] +dirs = "2.0.2" +json5 = "0.2.5" +rust-embed = "5.2.0" +serde = { version = "1.0.104", features = ["derive"] } +once_cell = "1.2.0" +sublime_fuzzy = "0.6.0" + +# Github deps +gtk-layer-shell-rs = { git = "https://github.com/subgraph/gtk-layer-shell-rs" } +gtk = { git = "https://github.com/gtk-rs/gtk.git" } +gdk = { git = "https://github.com/gtk-rs/gdk.git" } +gdk-pixbuf = { git = "https://github.com/gtk-rs/gdk-pixbuf.git" } +gio = { git = "https://github.com/gtk-rs/gio.git" } +pango = { git = "https://github.com/gtk-rs/pango.git" } diff --git a/README.md b/README.md index 1034d50..2f6e54f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Wayland compatible, touch friendly application launcher ![waffy-screenshot](https://i.imgur.com/iyEQVqo.png) ## Features -- sway dedicated launcher +- wlroots dedicated launcher - Touch support - Read all application locations - Respect hidden apps @@ -18,18 +18,5 @@ Wayland compatible, touch friendly application launcher ```shell script git clone https://github.com/wvffle/waffy.git cd waffy -cmake -Bcmake-build-release -H. -DCMAKE_BUILD_TYPE=Release -cmake --build cmake-build-release --target all +cargo build --release ``` - -### Add to path -```shell script -echo "export PATH=$HOME/.local/share/waffy:$PATH" >> $HOME/.bash_profile -# or -sudo ln -s $HOME/.local/share/waffy/waffy /usr/local/bin/waffy -``` - - - -## Other information -- [Studies project info](/docs/project.md) diff --git a/docs/project.md b/docs/project.md deleted file mode 100644 index 7f9569c..0000000 --- a/docs/project.md +++ /dev/null @@ -1,21 +0,0 @@ -### Project information - -Key | Value ---- | --- -Author | Kasper Seweryn -Album number | 107920 -Group | 8 (PS16) -Year | 2019 / 2020 - -### Implementation information -Everything is inside the `*.h` files - -- [Structures example](/src/desktop_entry.h#L14) -- [File operation example](/src/config.c#L18) -- [List example](/src/desktop_entry.h#L28) -- [Function example](/src/utils.c#L36) -- [Recursive function example](/src/config.c#L59) - -### Open source -Well it turned out that I've found a bug in sway which is my main wayland client -- https://github.com/swaywm/sway/issues/4840 \ No newline at end of file diff --git a/res/default_config.json5 b/res/default_config.json5 new file mode 100644 index 0000000..b2e68fe --- /dev/null +++ b/res/default_config.json5 @@ -0,0 +1,19 @@ +// ___ ___ // +// /'___\ /'___\ // +// __ __ __ __ /\ \__//\ \__/ __ __ // +// /\ \/\ \/\ \ /'__`\ \ \ ,__\ \ ,__\/\ \/\ \ // +// \ \ \_/ \_/ \/\ \L\.\_\ \ \_/\ \ \_/\ \ \_\ \ // +// \ \___x___/'\ \__/.\_\\ \_\ \ \_\ \/`____ \ // +// \/__//__/ \/__/\/_/ \/_/ \/_/ `/___/> \ // +// /\___/ // +// config \/__/ // +{ + // Number of columns in launcher + columns: 4, + + // Enable pywal colors + enable_pywal: false, + + // Search box prompt + search_prompt: 'search:', +} diff --git a/assets/main.css b/res/default_style.css similarity index 87% rename from assets/main.css rename to res/default_style.css index e21668c..791849a 100644 --- a/assets/main.css +++ b/res/default_style.css @@ -26,8 +26,10 @@ textview text { #apps > button { min-width: 270px; - transition: none; + border: none; + transition: none; + box-shadow: none; } /* default colors */ @@ -43,6 +45,10 @@ window { background: alpha(#fff, .3); } +.button-highlight { + color: #00aaff; +} + /* pywal colors */ window.pywal { background: alpha(@background, .7); @@ -55,3 +61,7 @@ window.pywal #apps > button { window.pywal #apps > button.active { background: @color5; } + +.button-highlight { + color: @color3; +} diff --git a/src/colors.c b/src/colors.c deleted file mode 100644 index acc4283..0000000 --- a/src/colors.c +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by waff on 1/8/20. -// - -#include "colors.h" -#include "config.h" - -char* get_css () { - open_config(); - - char* css_file = str_concat(get_user_home(), "/.config/waffy/main.css"); - - FILE* fp = fopen(css_file, "r"); - if (fp == NULL) { - fp = fopen(css_file, "w"); - fprintf(fp, "%s", read_file("../assets/main.css")); - } - - fclose(fp); - - if (config_wal == 1) { - char* wal_file = str_concat(get_user_home(), "/.cache/wal/colors-waybar.css"); - return str_concat(read_file(wal_file), read_file(css_file)); - } - - return read_file(css_file); -} \ No newline at end of file diff --git a/src/colors.h b/src/colors.h deleted file mode 100644 index 0ca5dd9..0000000 --- a/src/colors.h +++ /dev/null @@ -1,12 +0,0 @@ -// -// Created by waff on 1/8/20. -// - -#ifndef WAFFY_COLORS_H -#define WAFFY_COLORS_H - -#include "utils.h" - -char* get_css (); - -#endif //WAFFY_COLORS_H diff --git a/src/config.c b/src/config.c deleted file mode 100644 index 06e4c24..0000000 --- a/src/config.c +++ /dev/null @@ -1,114 +0,0 @@ -// -// Created by waff on 1/7/20. -// - -#include "config.h" - -char* config_dir = NULL; - -// Default config -long config_columns = 4; -long config_wal = 0; -char* config_prompt = "search:"; - -void get_long (char* value, long* res); -void get_str(char* value, char** res); -void get_bool(char* value, long* res); - -void open_config() { - if (config_dir != NULL) return; - config_dir = str_concat(get_user_home(), "/.config/waffy"); - mkdir(config_dir, 0755); - - char* file = str_concat(get_user_home(), "/.config/waffy/config"); - FILE* fp = fopen(file, "r"); - - if (fp == NULL) { - write_config(); - return; - } - - size_t line_number = 0; - while (!feof(fp)) { - char line[MAX_CONFIG_LINE_LENGTH]; - fscanf(fp, "%[^\n]\n", line); - line_number += 1; - - char* opline = str_trim(line); - if (opline[0] == '#') { - // pass - } else { - char* key = strtok(line, " "); - char* value = strtok(NULL, " "); - - if (strcmp(key, "columns") == 0) { - get_long(value, &config_columns); - } else if (strcmp(key, "prompt") == 0) { - get_str(value, &config_prompt); - } else if (strcmp(key, "wal_enable") == 0) { - get_bool(value, &config_wal); - } else { - fprintf(stderr, "Unknown config option '%s' on line %ld", key, line_number); - } - } - } - - fclose(fp); -} - -void get_str(char* value, char** res) { - if (value[0] != '"') { - char* buf = malloc(sizeof(char)* strlen(value)); - strcpy(buf, value); - *res = buf; - return; - } - - if (str_ends_with(value, "\"")) { - char* buf = malloc(sizeof(char)* (strlen(value) - 2)); - value[strlen(value) - 1] = '\0'; - strcpy(buf, value + 1); - *res = buf; - return; - } - - char* buf = strtok(NULL, " "); - get_str(str_concat(value, str_concat(" ", buf)), res); -} - -void get_bool(char* value, long* res) { - if (strcmp(value, "1") == 0 || strcmp(value, "yes") == 0 || strcmp(value, "on") == 0 || strcmp(value, "true") == 0) { - *res = 1; - return; - } - - *res = 0; -} - -void get_long(char* value, long* res) { - *res = strtol(value, NULL, 10); -} - -void write_config() { - char* file = str_concat(config_dir, "/config"); - FILE* fp = fopen(file, "w"); - - fprintf(fp, "# ___ ___ #\n"); - fprintf(fp, "# /'___\\ /'___\\ #\n"); - fprintf(fp, "# __ __ __ __ /\\ \\__//\\ \\__/ __ __ #\n"); - fprintf(fp, "# /\\ \\/\\ \\/\\ \\ /'__`\\ \\ \\ ,__\\ \\ ,__\\/\\ \\/\\ \\ #\n"); - fprintf(fp, "# \\ \\ \\_/ \\_/ \\/\\ \\L\\.\\_\\ \\ \\_/\\ \\ \\_/\\ \\ \\_\\ \\ #\n"); - fprintf(fp, "# \\ \\___x___/'\\ \\__/.\\_\\\\ \\_\\ \\ \\_\\ \\/`____ \\ #\n"); - fprintf(fp, "# \\/__//__/ \\/__/\\/_/ \\/_/ \\/_/ `/___/> \\ #\n"); - fprintf(fp, "# /\\___/ #\n"); - fprintf(fp, "# config \\/__/ #\n\n"); - fprintf(fp, "# Number of columns in launcher\n"); - fprintf(fp, "columns %ld\n\n", config_columns); - fprintf(fp, "# Enable pywal colors\n"); - fprintf(fp, "wal_enable false\n\n"); - fprintf(fp, "# Run prompt text\n"); - fprintf(fp, "prompt \"%s\"\n", config_prompt); - - fclose(fp); -} - diff --git a/src/config.h b/src/config.h deleted file mode 100644 index 5245cb0..0000000 --- a/src/config.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Created by waff on 1/7/20. -// - -#ifndef WAFFY_CONFIG_H -#define WAFFY_CONFIG_H - -#define MAX_CONFIG_LINE_LENGTH 256 - -#include -#include "utils.h" - -long config_columns; -long config_wal; -char* config_prompt; - -// Write current config to the file -void write_config(); - -// Read config from the file -void open_config (); - -#endif //WAFFY_CONFIG_H diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..9d5a7c5 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,61 @@ +use once_cell::sync::OnceCell; +use serde::Deserialize; +use std::fs; +use std::path::PathBuf; + +use super::resource::Resource; + +static CONFIG: OnceCell = OnceCell::new(); + +#[derive(Deserialize)] +pub struct Config { + pub columns: u8, + pub search_prompt: String, + pub enable_pywal: bool, +} + +impl Config { + pub fn create_dir() { + if let Some(mut config_dir) = dirs::config_dir() { + config_dir.push("waffy"); + if !config_dir.exists() { + let _ = fs::create_dir_all(config_dir); + } + } + } + + pub fn default(path_to_save: Option) -> Self { + // let file = Resource::get("default_config.json5").unwrap(); + // let content = String::from_utf8(file.as_ref().to_vec()) + let content = + Resource::from_file("default_config.json5").expect("Cannot read default config"); + + let config = json5::from_str::(&content).expect("Cannot parse default config"); + if let Some(path) = path_to_save { + let _ = fs::write(path, &content); + } + + config + } + + pub fn get_uncached() -> Self { + if let Some(mut config_path) = dirs::config_dir() { + config_path.push("waffy"); + config_path.push("config"); + + if !config_path.exists() { + return Self::default(Some(config_path)); + } + + let content = fs::read_to_string(config_path).expect("Could not read config"); + let config = json5::from_str::(&content).expect("Could not parse config"); + return config; + } + + Self::default(None) + } + + pub fn get() -> &'static Self { + CONFIG.get_or_init(Self::get_uncached) + } +} diff --git a/src/desktop_entry.c b/src/desktop_entry.c deleted file mode 100644 index 425d30c..0000000 --- a/src/desktop_entry.c +++ /dev/null @@ -1,167 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#include "desktop_entry.h" - -size_t deb_push(desktop_entry_batch* batch, desktop_entry* entry) { - desktop_entry_batch_node* node = malloc(sizeof(desktop_entry_batch_node)); - node->entry = entry; - node->next = NULL; - node->prev = NULL; - - if (batch->length == 0) { - batch->first = node; - batch->last = node; - return ++batch->length; - } - - batch->last->next = node; - node->prev = batch->last; - batch->last = node; - - return ++batch->length; -} - -size_t deb_unshift(desktop_entry_batch* batch, desktop_entry* entry) { - desktop_entry_batch_node* node = malloc(sizeof(desktop_entry_batch_node)); - node->entry = entry; - node->next = NULL; - node->prev = NULL; - - if (batch->length == 0) { - batch->first = node; - batch->last = node; - return ++batch->length; - } - - node->next = batch->first; - batch->first->prev = node; - batch->first = node; - - return ++batch->length; -} - -desktop_entry* deb_pop(desktop_entry_batch* batch) { - if (batch->length == 0) return NULL; - - desktop_entry_batch_node* node = batch->last; - batch->last = node->prev; - node->prev->next = NULL; - node->prev = NULL; - - if (--batch->length == 0) { - batch->first = NULL; - } - - desktop_entry* entry = node->entry; - - free(node); - return entry; -} - -desktop_entry* deb_shift(desktop_entry_batch* batch) { - if (batch->length == 0) return NULL; - - desktop_entry_batch_node* node = batch->first; - batch->first = node->next; - node->next->prev = NULL; - node->next = NULL; - - if (--batch->length == 0) { - batch->last = NULL; - } - - desktop_entry* entry = node->entry; - - free(node); - return entry; -} - -void deb_concat(desktop_entry_batch* target, desktop_entry_batch* source) { - if (target == NULL) { - fprintf(stderr, "[ERROR] target is NULL"); - return; - } - - if (source == NULL) { - return; - }; - - if (source->length == 0) { - deb_destructor(source); - return; - } - - if (target->length == 0) { - target->first = source->first; - target->last = source->last; - target->length = source->length; - - source->first = NULL; - source->last = NULL; - deb_destructor(source); - return; - } - - source->first->prev = target->last; - target->last->next = source->first; - target->last = source->last; - - target->length += source->length; - - source->first = NULL; - source->last = NULL; - deb_destructor(source); -} - -desktop_entry_batch* deb_constructor() { - desktop_entry_batch* batch = malloc(sizeof(desktop_entry_batch)); - - batch->first = NULL; - batch->last = NULL; - batch->length = 0; - - return batch; -} - -void deb_destructor(desktop_entry_batch* batch) { - if (batch->length) { - desktop_entry_batch_node* curr = batch->first; - - while (curr != NULL) { - free(curr->entry); - - desktop_entry_batch_node* next = curr->next; - free(curr); - - curr = next; - } - - } - - free(batch); -} - -void deb_destructor_no_entry(desktop_entry_batch* batch) { - if (batch->length) { - desktop_entry_batch_node* curr = batch->first; - - while (curr != NULL) { - desktop_entry_batch_node* next = curr->next; - free(curr); - - curr = next; - } - - } - - free(batch); -} - -desktop_entry* de_constructor(const char* gtk_launch_name) { - desktop_entry* entry = malloc(sizeof(desktop_entry)); - strcpy(entry->gtk_launch_name, gtk_launch_name); - - return entry; -} diff --git a/src/desktop_entry.h b/src/desktop_entry.h deleted file mode 100644 index be88004..0000000 --- a/src/desktop_entry.h +++ /dev/null @@ -1,62 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#ifndef WAFFY_DESKTOP_ENTRY_H -#define WAFFY_DESKTOP_ENTRY_H - -#include -#include -#include -#include - -// Desktop entry structure -typedef struct desktop_entry { - char gtk_launch_name[FILENAME_MAX]; // Used to find GtkAppInfo - char name[32]; // App display name - char icon[PATH_MAX]; // App icon -} desktop_entry; - -// Two-way list node -typedef struct desktop_entry_batch_node { - struct desktop_entry* entry; // Desktop entry - struct desktop_entry_batch_node* next; // Next node - struct desktop_entry_batch_node* prev; // Previous node -} desktop_entry_batch_node; - -// Two-way list starter -typedef struct desktop_entry_batch { - size_t length; // List length - struct desktop_entry_batch_node* first; // First node on the list - struct desktop_entry_batch_node* last; // Last node on the list -} desktop_entry_batch; - -// Helper function to create new desktop entry and allocate memory -desktop_entry* de_constructor (const char* gtk_launch_name); - -// Helper function to create new list and allocate memory -desktop_entry_batch* deb_constructor (); - -// Helper function to clean the list -void deb_destructor (desktop_entry_batch* batch); - -// Helper function to clean the list without cleaning entries -void deb_destructor_no_entry(desktop_entry_batch* batch); - - -// Helper function to add entry to the end of a list -size_t deb_push (desktop_entry_batch* batch, desktop_entry* entry); - -// Helper function to add entry to the beginning of a list -size_t deb_unshift (desktop_entry_batch* batch, desktop_entry* entry); - -// Helper function to remove entry from the end of a list -desktop_entry* deb_pop (desktop_entry_batch* batch); - -// Helper function to remove entry from the beginning of a list -desktop_entry* deb_shift (desktop_entry_batch* batch); - -// Helper function to join source to target list. Invalidates source list -void deb_concat (desktop_entry_batch* target, desktop_entry_batch* source); - -#endif //WAFFY_DESKTOP_ENTRY_H diff --git a/src/desktop_entry.rs b/src/desktop_entry.rs new file mode 100644 index 0000000..4fd9267 --- /dev/null +++ b/src/desktop_entry.rs @@ -0,0 +1,179 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use gtk::{IconTheme, IconThemeExt, Image, ImageExt, Label}; +use std::path::PathBuf; +use std::{fs, io}; + +use super::grid::GridButton; + +pub struct DesktopEntry { + pub name: String, + pub display_name: String, + pub icon_path: Option, +} + +impl DesktopEntry { + pub fn empty() -> Self { + Self { + name: String::from(""), + display_name: String::from(""), + icon_path: None, + } + } + + pub fn from_file(path: PathBuf) -> io::Result> { + let content = fs::read_to_string(path)?; + + let mut entry = Self::empty(); + let mut entry_section = false; + + for line in content.lines() { + let mut chars = line.chars(); + let first_char = chars.next(); + + if let Some(chr) = first_char { + if chr == '\0' || chr == '#' { + continue; + } + } + + if entry_section { + if let Some(chr) = first_char { + if chr == '[' { + break; + } + } + + let split = line.splitn(2, '=').map(String::from).collect::>(); + + let key = match split.get(0) { + Some(key) => key, + None => continue, + }; + + let value = match split.get(1) { + Some(value) => value, + None => continue, + }; + + if (key == "NoDisplay" || key == "Hidden") && value == "true" { + return Ok(None); + } + + if key == "Name" { + entry.set_name(value); + continue; + } + + if key == "Icon" { + entry.set_icon(value); + continue; + } + } else if line == "[Desktop Entry]" { + entry_section = true; + } + } + + Ok(Some(entry)) + } + + pub fn get_dirs() -> Vec { + let mut dirs: Vec = vec![ + "/usr/share/applications".into(), + "/usr/local/share/applications".into(), + ]; + + if let Some(user_dir) = dirs::home_dir() { + dirs.push(user_dir.join(".local/share/applications/")); + } + + dirs + } + + pub fn get_all() -> Vec>> { + let mut desktop_entries: Vec>> = Vec::with_capacity(10); + + for dir in Self::get_dirs() { + if let Ok(read_dir) = fs::read_dir(dir) { + for entry in read_dir { + if let Ok(entry) = entry { + let path = entry.path(); + let extension = path.extension(); + + match extension { + Some(extension) => { + if extension != "desktop" { + continue; + } + } + None => continue, + } + + if let Ok(desktop_entry) = Self::from_file(path) { + if let Some(desktop_entry) = desktop_entry { + desktop_entries.push(Rc::new(RefCell::new(desktop_entry))); + } + } + } + } + } + } + + desktop_entries + } + + pub fn set_name>(&mut self, name: S) { + self.name = name.into(); + self.display_name = self.name.clone(); + } + + pub fn set_icon>(&mut self, icon: S) { + self.icon_path = Some(icon.into()); + } +} + +impl GridButton for DesktopEntry { + fn label(&self) -> &String { + &self.name + } + + fn display_label(&self) -> Label { + Label::new(Some(&self.display_name)) + } + + fn icon(&self) -> Image { + if self.icon_path == None { + // gtk::IconSize::Dialog is 48x48 + return Image::new_from_icon_name( + Some("application-x-executable"), + gtk::IconSize::Dialog, + ); + } + + let icon = self.icon_path.as_ref().unwrap(); + if PathBuf::from(&icon).exists() { + let image = Image::new_from_file(&icon); + + // Scale down to 48x48 + if let Some(pixbuf) = image.get_pixbuf() { + if let Some(pixbuf) = pixbuf.scale_simple(48, 48, gdk_pixbuf::InterpType::Tiles) { + return Image::new_from_pixbuf(Some(&pixbuf)); + } + } + } + + if let Some(theme) = IconTheme::get_default() { + let info = theme.load_icon(&icon, 48, gtk::IconLookupFlags::FORCE_SIZE); + + if let Ok(info) = info { + if let Some(pixbuf) = info { + return Image::new_from_pixbuf(Some(&pixbuf)); + } + } + } + + // gtk::IconSize::Dialog is 48x48 + Image::new_from_icon_name(Some("application-x-executable"), gtk::IconSize::Dialog) + } +} diff --git a/src/filter.c b/src/filter.c deleted file mode 100644 index 8f6beb1..0000000 --- a/src/filter.c +++ /dev/null @@ -1,54 +0,0 @@ -// -// Created by waff on 12/22/19. -// - -#include "filter.h" - -desktop_entry_batch* filter_apps(desktop_entry_batch* hay, const char* needle, enum filter_mode mode) { - switch (mode) { - case CASE_INSENSITIVE: return filter_case_insensitive(hay, needle); - case FUZZY: return filter_fuzzy(hay, needle); - default: return NULL; - } -} - -desktop_entry_batch* filter_fuzzy(desktop_entry_batch* hay, const char* needle) { - if (hay == NULL) return NULL; - if (needle == NULL || !strlen(needle)) return hay; - - desktop_entry_batch_node* curr = hay->first; - if (curr == NULL) return NULL; - - desktop_entry_batch* res = deb_constructor(); - while (curr != NULL) { - if (str_fuzzy_match(curr->entry->name, needle)) { - deb_push(res, curr->entry); - } - - curr = curr->next; - } - - if (res->length == 0) return NULL; - return res; -} - -desktop_entry_batch* filter_case_insensitive(desktop_entry_batch* hay, const char* needle) { - if (hay == NULL) return NULL; - if (needle == NULL || !strlen(needle)) return hay; - - desktop_entry_batch_node* curr = hay->first; - if (curr == NULL) return NULL; - - desktop_entry_batch* res = deb_constructor(); - while (curr != NULL) { - - if (strcasestr(curr->entry->name, needle) != NULL) { - deb_push(res, curr->entry); - } - - curr = curr->next; - } - - if (res->length == 0) return NULL; - return res; -} diff --git a/src/filter.h b/src/filter.h deleted file mode 100644 index aba2cf0..0000000 --- a/src/filter.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by waff on 12/22/19. -// - -#ifndef WAFFY_FILTER_H -#define WAFFY_FILTER_H - -#define _GNU_SOURCE -#include "desktop_entry.h" -#include "utils.h" - -enum filter_mode { - CASE_INSENSITIVE, - FUZZY -}; - -// Filter apps by needle using provided mode -desktop_entry_batch* filter_apps (desktop_entry_batch* hay, const char* needle, enum filter_mode mode); - -// Fuzzy filter apps by needle -desktop_entry_batch* filter_fuzzy (desktop_entry_batch* hay, const char* needle); - -// Case insensitive filter apps by needle -desktop_entry_batch* filter_case_insensitive (desktop_entry_batch* hay, const char* needle); - -#endif //WAFFY_FILTER_H diff --git a/src/find_desktop.c b/src/find_desktop.c deleted file mode 100644 index 54bd239..0000000 --- a/src/find_desktop.c +++ /dev/null @@ -1,116 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#include "find_desktop.h" - -char* get_user_desktop_files_directory() { - char* homedir = get_user_home(); - char* local_desktop_directory = "/.local/share/applications/"; - return str_concat(homedir, local_desktop_directory); -} - -desktop_entry* read_desktop_file(const char* file_path, const char* gtk_launch_name) { - FILE* fp = fopen(file_path, "r"); - if (!fp) return NULL; - - desktop_entry* entry = de_constructor(gtk_launch_name); - - char in_entry_section = 0; - char line[PATH_MAX]; - while (!feof(fp)) { - fscanf(fp, "%[^\n]\n", line); - - // Skip comments and empty lines - if (line[0] == '\0' || line[0] == '#') continue; - - if (in_entry_section) { - // Another section occured, stop parsing - if (line[0] == '[') break; - - char* key = strtok(line, "="); - char* value = strtok(NULL, "="); - - // Skip malformed field - if (!value) continue; - - if (!strcmp(key, "NoDisplay") && !strcmp(value, "true")) { - free(entry); - return NULL; - } - - if (!strcmp(key, "Name")) { - strcpy(entry->name, value); - continue; - } - - if (!strcmp(key, "Icon")) { - strcpy(entry->icon, value); - continue; - } - - } else if (!strcmp(line, "[Desktop Entry]")) { - in_entry_section = 1; - } - } - - fclose(fp); - return entry; -} - -desktop_entry_batch* find_desktop_files(const char* directory) { - DIR* dir_handle = opendir(directory); - - // Skip non-existant directory - if (dir_handle == NULL) { - return NULL; - } - - struct dirent* dp; - desktop_entry_batch* batch = deb_constructor(); - while ((dp = readdir(dir_handle)) != NULL) { - if (!str_ends_with(dp->d_name, ".desktop")) continue; - - char* file_path = str_concat(directory, dp->d_name); - - // Remove .desktop from end -// size_t len = strlen(dp->d_name) - 8; -// char gtk_launch_name[len + 1]; -// strncpy(gtk_launch_name, dp->d_name, len); -// gtk_launch_name[len] = '\0'; - - desktop_entry* entry = read_desktop_file(file_path, dp->d_name); - -// desktop_entry* entry = read_desktop_file(file_path, gtk_launch_name); - if (entry != NULL) deb_push(batch, entry); - - free(file_path); - } - - closedir(dir_handle); - return batch; -} - -desktop_entry_batch* find_all_desktop_files() { - char* user_dir = get_user_desktop_files_directory(); - char* desktop_entry_dirs[DESKTOP_ENTRY_DIRS_NUM] = { - "/usr/share/applications/", - "/usr/local/share/applications/", - user_dir - }; - - - desktop_entry_batch* all_entries = deb_constructor(); - - for (size_t i = 0; i < DESKTOP_ENTRY_DIRS_NUM; ++i) { - desktop_entry_batch* entries = find_desktop_files(desktop_entry_dirs[i]); - if (entries == NULL) continue; - - deb_concat(all_entries, entries); - } - - free(user_dir); - - return all_entries; -} - diff --git a/src/find_desktop.h b/src/find_desktop.h deleted file mode 100644 index e48f6d3..0000000 --- a/src/find_desktop.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#ifndef WAFFY_FIND_DESKTOP_H -#define WAFFY_FIND_DESKTOP_H - -#include -#include "utils.h" -#include "desktop_entry.h" - -// 2 system directories and 1 user directory -#define DESKTOP_ENTRY_DIRS_NUM 3 - -// Returns ~/.local/share/applications -char* get_user_desktop_files_directory (); - -// Opens a desktop file and creates a desktop entry -desktop_entry* read_desktop_file (const char* file_path, const char* gtk_launch_name); - -// Reads all desktop files in a directory -desktop_entry_batch* find_desktop_files (const char* directory); - -// Reads all desktop files in all of the directories -desktop_entry_batch* find_all_desktop_files (); - -#endif //WAFFY_FIND_DESKTOP_H diff --git a/src/grid.rs b/src/grid.rs new file mode 100644 index 0000000..ffaf364 --- /dev/null +++ b/src/grid.rs @@ -0,0 +1,264 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use gtk::{ButtonExt, ContainerExt, Grid as GtkGrid, GridExt, LabelExt, ScrolledWindow as GtkWindow, StyleContextExt, Viewport as GtkViewport, WidgetExt}; + +use sublime_fuzzy::{ + best_match as fuzzy_match, + format_simple as fuzzy_format, +}; + +use super::Config; + +pub const SHOW_ICON: u32 = 0b01; +pub const SHOW_LABEL: u32 = 0b10; + +type GridButtonRc = Rc>; +type GridButtonCallback = dyn Fn(GridButtonRc); + +pub trait GridButton { + fn label(&self) -> &String; + fn display_label(&self) -> gtk::Label; + fn icon(&self) -> gtk::Image; +} + +pub trait GridCursor { + fn cursor_left(&mut self); + fn cursor_right(&mut self); + fn cursor_up(&mut self); + fn cursor_down(&mut self); + fn cursor_hide(&self); + fn cursor_show(&self); + fn cursor_set_pos(&mut self, x: usize, y: usize); + fn cursor_set_index(&mut self, index: usize); +} + +pub struct Grid { + buttons: Vec, + items: Vec, + pub window: GtkWindow, + pub grid: GtkGrid, + flags: u32, + cursor: (usize, usize), +} + +impl Grid { + pub fn new( + items: Vec, + flags: u32, + click_callback: Rc, + ) -> Self { + let adjustment = None::<>k::Adjustment>; + let window = GtkWindow::new(adjustment, adjustment); + let viewport = GtkViewport::new(adjustment, adjustment); + let grid = GtkGrid::new(); + + let config = Config::get(); + + // Initialize columns + for i in 0..(config.columns - 1) { + grid.insert_column(i as i32); + } + + grid.set_column_spacing(17); + grid.set_row_spacing(17); + grid.set_halign(gtk::Align::Center); + + window.add(&viewport); + viewport.add(&grid); + + let mut buttons: Vec = Vec::new(); + + let mut i = 0; + let mut local_items = Vec::new(); + for item in items { + local_items.push(item.clone()); + + let col = i as i32 % config.columns as i32; + let row = i as i32 / config.columns as i32; + i += 1; + + let widget = gtk::Button::new(); + let content = gtk::Grid::new(); + + content.set_column_spacing(17); + + if flags & SHOW_ICON > 0 { + content.insert_column(0); + content.attach(&item.borrow().icon(), 0, 0, 1, 1); + } + + if flags & SHOW_LABEL > 0 { + let label = item.borrow().display_label(); + content.insert_column(1); + content.attach(&label, 1, 0, 1, 1); + + label.set_max_width_chars(16); + label.set_ellipsize(pango::EllipsizeMode::End); + label.set_use_markup(true); + } + + let callback = click_callback.clone(); + + widget.connect_clicked(move |_| { + (callback)(item.clone()); + }); + + widget.add(&content); + grid.attach(&widget, col, row, 1, 1); + buttons.push(widget); + } + + let mut res = Self { + buttons, + items: local_items, + window, + grid, + flags, + cursor: (0, 0) + }; + + for (i, widget) in res.buttons.iter().enumerate() { + widget.connect_enter_notify_event(move |widget, _| { + res.cursor_set_index(i); + gtk::Inhibit(true) + }); + + widget.connect_leave_notify_event(move |widget, _| { + res.cursor_hide(); + gtk::Inhibit(true) + }); + } + + res + } + + pub fn filter(&self, needle: String) { + self.update(needle); + } + + pub fn update(&self, needle: String) { + let mut sorted_entries = Vec::with_capacity(self.items.len()); + let columns = Config::get().columns as i32; + self.grid.foreach(|widget| self.grid.remove(widget)); + + for (i, item) in self.items.iter().enumerate() { + let mut item = item.borrow_mut(); + let label = item.label().clone(); + let button = self.buttons.get(i).unwrap(); + + if needle == "" { + sorted_entries.push(true); + item.display_label().set_label(&label); + continue; + } + + if let Some(res) = fuzzy_match(needle.as_str(), label.as_str()) { + let name = fuzzy_format( + &res, + label.as_str(), + "[[", + "]]" + ); + + // TODO: Label is not setting for some weird reason + item.display_label().set_label(name.as_str()); + println!("{} {}", item.display_label().get_label().unwrap(), name.as_str()); + + sorted_entries.push(true); + continue; + } + + sorted_entries.push(false); + } + + let mut idx = 0; + for (i, visible) in sorted_entries.into_iter().enumerate() { + let mut col = i as i32 % columns; + let mut row = i as i32 / columns; + let button = self.buttons.get(i).unwrap(); + + if visible { + col = idx as i32 % columns; + row = idx as i32 / columns; + + self.grid.attach(button, col, row, 1, 1); + idx += 1; + } + + } + + self.grid.show(); + } +} + +impl GridCursor for Grid { + fn cursor_left(&mut self) { + if self.cursor.0 > 0 { + self.cursor_set_pos(self.cursor.0 - 1, self.cursor.1) + } + } + + fn cursor_right(&mut self) { + self.cursor_set_pos(self.cursor.0 + 1, self.cursor.1) + } + + fn cursor_up(&mut self) { + if self.cursor.1 > 0 { + self.cursor_set_pos(self.cursor.0, self.cursor.1 - 1) + } + } + + fn cursor_down(&mut self) { + self.cursor_set_pos(self.cursor.0, self.cursor.1 + 1) + } + + fn cursor_hide(&self) { + let config = Config::get(); + if let Some(widget) = self.buttons.get(self.cursor.1 * config.columns as usize + self.cursor.0) { + let ctx = widget.get_style_context(); + ctx.remove_class("active"); + } + } + + fn cursor_show(&self) { + let config = Config::get(); + if let Some(widget) = self.buttons.get(self.cursor.1 * config.columns as usize + self.cursor.0) { + let ctx = widget.get_style_context(); + ctx.add_class("active"); + } + } + + fn cursor_set_pos(&mut self, x: usize, y: usize) { + let config = Config::get(); + + let idx = y * config.columns as usize + x; + + let mut x_next = idx % config.columns as usize; + let mut y_next = idx / config.columns as usize; + + let y_max = self.buttons.len() - 1; + if y_next > y_max { + y_next = y_max; + } + + if self.cursor.0 != x_next && self.cursor.1 != y_next { + self.cursor_hide(); + + self.cursor.0 = x_next; + self.cursor.1 = y_next; + + self.cursor_show(); + } + } + + fn cursor_set_index(&mut self, i: usize) { + let config = Config::get(); + + let x = i % config.columns as usize; + let y = i / config.columns as usize; + + self.cursor_set_pos(x, y); + } +} + diff --git a/src/main.c b/src/main.c deleted file mode 100644 index cc4db6f..0000000 --- a/src/main.c +++ /dev/null @@ -1,382 +0,0 @@ -#include - -#include -#include -#include -#include - -#include "find_desktop.h" -#include "filter.h" -#include "config.h" -#include "colors.h" - -#define ICON_SIZE 48 -#define MAX_CHARS 16 -#define COL_PADDING 17 - -desktop_entry_batch* all_desktop_entries = NULL; -desktop_entry_batch* filtered = NULL; -GtkGrid* app_grid = NULL; -GtkGrid* fav_grid = NULL; -GtkWidget* window; -GtkWidget* search_input; -uint current_items = 0; -int current_item = 0; -int current_item_kb = 0; - -// HACK: Get over with the fact that after changing CSS class, button gets rehovered -int last_current_item = -1; -int last_current_item_kb = 0; - -GdkCursor* arrow; -GdkCursor* pointer; - -int get_monitor_width () { - GdkWindow* gdk_window = gtk_widget_get_window(window); - GdkDisplay* display = gtk_widget_get_display(window); - GdkMonitor* monitor = gdk_display_get_monitor_at_window(display, gdk_window); - GdkRectangle rect; - gdk_monitor_get_geometry(monitor, &rect); - return rect.width; -} - -void add_class (GtkWidget* widget, const char* class_name) { - GtkStyleContext* context = gtk_widget_get_style_context(widget); - gtk_style_context_add_class(context, class_name); -} - -void window_destroy (GtkWidget* widget, gpointer* data) { - deb_destructor(all_desktop_entries); - gtk_main_quit(); -} - -void update_current () { - int idx = -1; - - if (current_item != last_current_item) { - last_current_item = current_item; - last_current_item_kb = current_item; - current_item_kb = current_item; - idx = current_item; - } else if (current_item_kb != last_current_item_kb) { - last_current_item_kb = current_item_kb; - idx = current_item_kb; - } - - if (idx == -1) return; - for (size_t i = 0; i < current_items; ++i) { - GtkWidget* app = gtk_grid_get_child_at(app_grid, i % config_columns, i / config_columns); - if (app == NULL) continue; - GtkStyleContext* ctx = gtk_widget_get_style_context(app); - - if (gtk_style_context_has_class(ctx, "active") == TRUE) { - gtk_style_context_remove_class(ctx, "active"); - } - - if (i == idx) { - gtk_style_context_add_class(ctx, "active"); - } - } -} - -void window_enter (GtkWidget* widget, GdkEvent* event, int* data) { - gtk_layer_set_keyboard_interactivity(GTK_WINDOW(window), TRUE); -} - -void window_leave (GtkWidget* widget, GdkEventCrossing* event, int* data) { - // Check if we leave the window itself - if (event->detail != GDK_NOTIFY_NONLINEAR) return; - gtk_layer_set_keyboard_interactivity(GTK_WINDOW(window), FALSE); -} - -void app_enter (GtkWidget* widget, GdkEvent* event, int* data) { - gdk_window_set_cursor(gtk_widget_get_window(widget), pointer); - current_item =* data; - update_current(); -} - -void app_leave (GtkWidget* widget, GdkEvent* event, int* data) { - gdk_window_set_cursor(gtk_widget_get_window(widget), arrow); - current_item =* data; - update_current(); -} - -void app_clicked (GtkButton* button, desktop_entry* entry) { - printf("run: %s\n", entry->name); - GDesktopAppInfo* info = g_desktop_app_info_new(entry->gtk_launch_name); - - if (info != NULL) { - g_autoptr(GError) error = NULL; - g_app_info_launch(G_APP_INFO(info), NULL, NULL, &error); - - if (filtered != all_desktop_entries && filtered != NULL) { - deb_destructor_no_entry(filtered); - } - - if (error != NULL) { - window_destroy(NULL, NULL); - g_error("Could not run program '%s': %s", entry->name, error->message); - } - - window_destroy(NULL, NULL); - exit(EXIT_SUCCESS); - } - - if (filtered != all_desktop_entries && filtered != NULL) { - deb_destructor_no_entry(filtered); - } - - window_destroy(NULL, NULL); - g_error("Could not run program '%s': Cannot fetch AppInfo", entry->name); -} - -void update_apps (desktop_entry_batch* apps) { - // Reset grid - for (size_t i = 0; i < current_items; ++i) { - GtkWidget* app = gtk_grid_get_child_at(app_grid, i % config_columns, i / config_columns); - gtk_container_remove(GTK_CONTAINER(app_grid), app); - } - - int old_current_items = current_items; - if (apps == NULL || (current_items = apps->length) == 0) { - current_items = 0; - return; - } - - if (old_current_items != current_items) { - current_item = 0; - current_item_kb = 0; - update_current(); - } - - int n = 0; - desktop_entry_batch_node* curr = apps->first; - while (curr != NULL) { - GtkIconTheme* theme = gtk_icon_theme_get_default(); - GtkWidget* icon = NULL; - GdkPixbuf* pixbuf = NULL; - - if (is_path(curr->entry->icon)) { - GtkWidget* image = gtk_image_new_from_file(curr->entry->icon); - pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image)); -// gtk_widget_destroy(image); - } else { - const gchar** icons = g_themed_icon_get_names((GThemedIcon* ) g_themed_icon_new(curr->entry->icon)); - GtkIconInfo* info = gtk_icon_theme_choose_icon(theme, icons, ICON_SIZE, GTK_ICON_LOOKUP_FORCE_SIZE); - - if (info != NULL) { - GtkWidget* image = gtk_image_new_from_file(gtk_icon_info_get_filename(info)); - pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image)); - } - - } - - if (GDK_IS_PIXBUF(pixbuf)) { - pixbuf = gdk_pixbuf_scale_simple(pixbuf, ICON_SIZE, ICON_SIZE, GDK_INTERP_TILES); - icon = gtk_image_new_from_pixbuf(pixbuf); - } - - if (icon == NULL) { - icon = gtk_image_new_from_icon_name("application-x-executable", GTK_ICON_SIZE_DIALOG); -// pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image)); -// pixbuf = gdk_pixbuf_scale_simple(pixbuf, ICON_SIZE, ICON_SIZE, GDK_INTERP_TILES); -// icon = gtk_image_new_from_pixbuf(pixbuf); - } - - GtkWidget* app = gtk_button_new(); - GtkWidget* app_content = gtk_grid_new(); - GtkWidget* label = gtk_label_new(curr->entry->name); - - - gtk_grid_insert_column(GTK_GRID(app_content), 0); - gtk_grid_insert_column(GTK_GRID(app_content), 1); - - gtk_grid_set_column_spacing(GTK_GRID(app_content), COL_PADDING); - - gtk_grid_attach(GTK_GRID(app_content), icon, 0, 0, 1, 1); - gtk_grid_attach(GTK_GRID(app_content), label, 1, 0, 1, 1); - - - gtk_label_set_max_width_chars(GTK_LABEL(label), MAX_CHARS); - gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END); - - gtk_container_add(GTK_CONTAINER(app), app_content); - - uint col = n % config_columns; - uint row = n / config_columns; - gtk_grid_attach(app_grid, app, col, row, 1, 1); - gtk_widget_show_all(app); - - int* pos = malloc(sizeof(int)); - memcpy(pos, &n, sizeof(int)); - g_signal_connect(app, "clicked", G_CALLBACK(app_clicked), curr->entry); - g_signal_connect(app, "enter-notify-event", G_CALLBACK(app_enter), pos); - g_signal_connect(app, "leave-notify-event", G_CALLBACK(app_leave), pos); - - curr = curr->next; - n += 1; - } - - update_current(); -} - -gboolean key_pressed (GtkWidget* window, GdkEventKey* event, gpointer data) { - if (event->keyval == GDK_KEY_Escape) { - window_destroy(window, data); - return TRUE; - } - - if (event->keyval == GDK_KEY_Return) { - desktop_entry_batch* apps = filtered; - if (apps == NULL) apps = all_desktop_entries; - - desktop_entry_batch_node* curr = apps->first; - - int idx = (current_item != last_current_item) - ? current_item - : current_item_kb; - - while (curr != NULL) { - if (idx-- == 0) break; - curr = curr->next; - } - - app_clicked(NULL, curr->entry); - return TRUE; - } - - if (event->keyval == GDK_KEY_Left) { - if (current_item_kb == 0) return TRUE; - current_item_kb -= 1; -// update_current(); - return TRUE; - } - - if (event->keyval == GDK_KEY_Right) { - if (current_item_kb == current_items - 1) return TRUE; - current_item_kb += 1; -// update_current(); - return TRUE; - } - - if (event->keyval == GDK_KEY_Up) { - current_item_kb -= config_columns; - if (current_item_kb < 0) current_item_kb += config_columns; - -// update_current(); - return TRUE; - } - - if (event->keyval == GDK_KEY_Down) { - current_item_kb += config_columns; - if (current_item_kb > current_items - 1) current_item_kb -= config_columns; - -// update_current(); - return TRUE; - } - - return FALSE; -} -gboolean key_released (GtkWidget* window, GdkEventKey* event, gpointer data) { - // Normal key input - GtkTextBuffer* buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(search_input)); - GtkTextIter start; - GtkTextIter end; - gtk_text_buffer_get_bounds (buf, &start, &end); - - char* needle = gtk_text_buffer_get_text(buf, &start, &end, FALSE); - filtered = filter_apps(all_desktop_entries, needle, FUZZY); - update_apps(filtered); -} - -int main (int argc, char* argv[]) { - open_config(); - all_desktop_entries = find_all_desktop_files(); - - gtk_init(&argc, &argv); - - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_layer_init_for_window(GTK_WINDOW(window)); - gtk_layer_set_layer (GTK_WINDOW(window), GTK_LAYER_SHELL_LAYER_TOP); -// gtk_layer_set_keyboard_interactivity(GTK_WINDOW(window), TRUE); - - // HACK: Set window fullscreen - gtk_layer_set_anchor (GTK_WINDOW(window), 0, TRUE); - gtk_layer_set_anchor (GTK_WINDOW(window), 1, TRUE); - gtk_layer_set_anchor (GTK_WINDOW(window), 2, TRUE); - gtk_layer_set_anchor (GTK_WINDOW(window), 3, TRUE); - - gtk_widget_realize(window); - - // Set window options - gtk_window_set_resizable(GTK_WINDOW(window), FALSE); - gtk_window_set_decorated(GTK_WINDOW(window), FALSE); - - // Add event handlers - g_signal_connect(window, "destroy", G_CALLBACK(window_destroy), NULL); - g_signal_connect(window, "key_press_event", G_CALLBACK(key_pressed), NULL); - g_signal_connect(window, "key_release_event", G_CALLBACK(key_released), NULL); - g_signal_connect(window, "enter-notify-event", G_CALLBACK(window_enter), NULL); - g_signal_connect(window, "leave-notify-event", G_CALLBACK(window_leave), NULL); - - // Main layout - GtkBox* layout = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(layout)); - - // Add search field - GtkBox* search_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0)); - gtk_box_pack_start(layout, GTK_WIDGET(search_box), FALSE, FALSE, 0); - - // -- Add spacing - int grid_width = 270* config_columns + (config_columns + 2)* COL_PADDING; - GtkWidget* spacer = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_widget_set_size_request(spacer, (get_monitor_width() - grid_width) / 2, 1); - gtk_box_pack_start(search_box, spacer, FALSE, FALSE, 0); - - // -- Add label - GtkWidget* search_label = gtk_label_new(NULL); - gtk_label_set_markup(GTK_LABEL(search_label), config_prompt); - gtk_box_pack_start(search_box, search_label, FALSE, FALSE, 0); - - add_class(search_label, "textview-label"); - - // -- Add input - search_input = gtk_text_view_new(); - gtk_widget_set_name(search_input, "search"); - gtk_box_pack_start(search_box, search_input, TRUE, TRUE, 0); - - // Apps grid layout - app_grid = (GtkGrid* ) gtk_grid_new(); - gtk_box_pack_start(layout, GTK_WIDGET(app_grid), TRUE, TRUE, 0); - for (size_t i = 0; i < config_columns; ++i) { - gtk_grid_insert_column(app_grid, i); - } - gtk_grid_set_column_spacing(app_grid, COL_PADDING); - gtk_grid_set_row_spacing(app_grid, COL_PADDING); - gtk_widget_set_name(GTK_WIDGET(app_grid), "apps"); - - gtk_widget_set_halign(app_grid, GTK_ALIGN_CENTER); - - update_apps(all_desktop_entries); - - GtkCssProvider* css_provider = gtk_css_provider_new(); - - if (config_wal == 1) { - add_class(window, "pywal"); - } - - char* css = get_css(); - if (gtk_css_provider_load_from_data(css_provider, css, strlen(css), NULL)) { - gtk_style_context_add_provider_for_screen(gtk_widget_get_screen(window), - GTK_STYLE_PROVIDER(css_provider), - GTK_STYLE_PROVIDER_PRIORITY_USER); - } - - GdkDisplay* display = gtk_widget_get_display(window); - arrow = GDK_CURSOR(gdk_cursor_new_from_name(display, "default")); - pointer = GDK_CURSOR(gdk_cursor_new_from_name(display, "pointer")); - - gtk_window_set_title(GTK_WINDOW(window), "Waffy"); - gtk_widget_show_all(window); - gtk_main(); -} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b793416 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,158 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use gdk::*; +use gtk::{BoxExt, ContainerExt, CssProvider, CssProviderExt, GtkWindowExt, Inhibit, Label, LabelExt, Orientation, StyleContext, StyleContextExt, TextBuffer, TextBufferExt, TextTagTable, TextView, WidgetExt}; +use gtk_layer_shell_rs::*; + +use config::Config; +use desktop_entry::DesktopEntry; + +use crate::grid::GridButton; + +mod config; +mod grid; +mod resource; +mod style; + +mod desktop_entry; + +fn main() { + Config::create_dir(); + + let config = Config::get(); + let desktop_entries = DesktopEntry::get_all(); + + gtk::init().expect("Could not init GTK!"); + + let window = gtk::Window::new(gtk::WindowType::Toplevel); + init_for_window(&window); + set_layer(&window, Layer::Top); + set_anchor(&window, Edge::Top, true); + set_anchor(&window, Edge::Left, true); + set_anchor(&window, Edge::Right, true); + set_anchor(&window, Edge::Bottom, true); + + window.connect_key_release_event(|window, key| { + use gdk::enums::key; + + match key.get_keyval() { + key::Escape => { + window.destroy(); + std::process::exit(0) + } + _ => { + Inhibit(true) + }, + } + }); + + window.connect_enter_notify_event(|window, _event| { + set_keyboard_interactivity(window, true); + Inhibit(false) + }); + + window.connect_leave_notify_event(|window, event| { + if event.get_detail() == gdk::NotifyType::NonlinearVirtual { + set_keyboard_interactivity(window, false); + } + + Inhibit(false) + }); + + window.set_resizable(false); + window.set_decorated(false); + + let layout = gtk::Box::new(Orientation::Vertical, 0); + window.add(&layout); + + let search_box = gtk::Box::new(Orientation::Horizontal, 0); + layout.pack_start(&search_box, false, false, 0); + + let grid_width = 270 * 4 + (4 + -1) * 17; + let spacer = gtk::Box::new(Orientation::Horizontal, 0); + let width = get_monitor_width() / 2 - (grid_width as i32) / 2; + spacer.set_size_request(width, 1); + + search_box.pack_start(&spacer, false, false, 0); + + let search_label = Label::new(None); + search_label.set_markup(config.search_prompt.as_str()); + search_box.pack_start(&search_label, false, false, 0); + + add_class(&search_label, "textview-label"); + + let buttons = desktop_entries + .into_iter() + .map(|entry| entry as Rc>) + .collect::>(); + + let app_grid = Rc::new(RefCell::new(grid::Grid::new( + buttons, + grid::SHOW_ICON | grid::SHOW_LABEL, + Rc::new(|entry| { + println!("{:?}", entry.borrow().label()); + }), + ))); + + let buffer = TextBuffer::new(None::<&TextTagTable>); + let cloned_app_grid = app_grid.clone(); + + buffer.connect_changed(move |buf| { + let search_text = buf.get_text(&buf.get_start_iter(), &buf.get_end_iter(), false) + .unwrap_or("".into()); + + if let Ok(grid) = cloned_app_grid.try_borrow() { + println!("search text: {}", search_text); + grid.filter(search_text.to_string()); + } + }); + + + let search_input = TextView::new_with_buffer(&buffer); + search_box.pack_start(&search_input, true, true, 0); + + let grid = &app_grid.borrow().grid; + grid.set_widget_name("apps"); + + let gtk_window = &app_grid.borrow().window; + layout.pack_start(gtk_window, true, true, 0); + + let css_provider = CssProvider::new(); + + if config.enable_pywal { + add_class(&window, "pywal"); + } + + let css = style::get_css(); + + if let Ok(_) = css_provider.load_from_data(css.as_ref()) { + StyleContext::add_provider_for_screen( + &gdk::Screen::get_default().expect("Error initializing css provider"), + &css_provider, + gtk::STYLE_PROVIDER_PRIORITY_USER, + ); + } + + if let Some(display) = window.get_display() { + let arrow = + Cursor::new_from_name(&display, "default").expect("Could not create 'default' cursor!"); + + let pointer = + Cursor::new_from_name(&display, "pointer").expect("Could not create 'pointer' cursor!"); + } + + window.set_title("waffy"); + window.show_all(); + + gtk::main(); +} + +fn get_monitor_width() -> i32 { + return 1920; +} + +fn add_class(widget: &W, class_name: &str) { + let ctx = widget.get_style_context(); + ctx.add_class(class_name); +} diff --git a/src/resource.rs b/src/resource.rs new file mode 100644 index 0000000..72d669a --- /dev/null +++ b/src/resource.rs @@ -0,0 +1,13 @@ +use rust_embed::RustEmbed; +use std::string::FromUtf8Error; + +#[derive(RustEmbed)] +#[folder = "res/"] +pub struct Resource; + +impl Resource { + pub fn from_file(filename: &str) -> Result { + let file = Self::get(filename).unwrap(); + String::from_utf8(file.as_ref().to_vec()) + } +} diff --git a/src/style.rs b/src/style.rs new file mode 100644 index 0000000..297f85a --- /dev/null +++ b/src/style.rs @@ -0,0 +1,49 @@ +use std::fs; +use std::path::PathBuf; + +use super::resource::Resource; + +pub fn get_default_css(path_to_save: Option) -> String { + // let file = Resource::get("default_style.css").unwrap(); + // let content = String::from_utf8(file.as_ref().to_vec()) + + let content = Resource::from_file("default_style.css").expect("Cannot read default style"); + + if let Some(path) = path_to_save { + let _ = fs::write(path, &content); + } + + content +} + +pub fn get_css() -> String { + let mut content = String::from(""); + if let Some(mut style_path) = dirs::config_dir() { + style_path.push("waffy"); + style_path.push("style.css"); + + if style_path.exists() { + content = fs::read_to_string(style_path).expect("Could not read config"); + } else { + content = get_default_css(Some(style_path)) + } + } + + if content == "" { + content = get_default_css(None); + } + + // TODO: Check config option + if let Some(mut pywal_path) = dirs::cache_dir() { + pywal_path.push("wal"); + pywal_path.push("colors-waybar.css"); + + if pywal_path.exists() { + let mut pywal_content = fs::read_to_string(pywal_path).expect("Could not read config"); + pywal_content.push_str(content.as_str()); + content = pywal_content; + } + } + + content +} diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 8d84650..0000000 --- a/src/utils.c +++ /dev/null @@ -1,78 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#include "utils.h" - -char* read_file(const char* path) { - FILE* fp = fopen(path, "r"); - if (fp == NULL) return ""; - - size_t len; - char* res = NULL; - ssize_t bytes_read = getdelim(&res, &len, '\0', fp); - fclose(fp); - - if (bytes_read == -1) return ""; - return res; -} - -int str_starts_with (const char* hay, const char* needle) { - return strncmp(needle, hay, strlen(needle)) == 0; -} - -char* str_trim (const char* str) { - char* buf = malloc(strlen(str)* sizeof(char)); - sscanf(str, "%s", buf); - return buf; -} - -int str_ends_with (const char* hay, const char* needle) { - size_t hay_len = strlen(hay); - size_t needle_len = strlen(needle); - return hay_len >= needle_len && !strcmp(hay + (hay_len - needle_len), needle); -} - -char* str_concat (const char* string1, const char* string2) { - // NOTE: Additional 1 for \0 at the end - size_t length = 1 + strlen(string1) + strlen(string2); - - char* sum = malloc(length* sizeof(char)); - sprintf(sum, "%s%s", string1, string2); - - return sum; -} - -char* get_user_home () { - char* homedir; - if ((homedir = getenv("HOME")) == NULL) { - homedir = getpwuid(getuid())->pw_dir; - } - - return homedir; -} - -int str_fuzzy_match(const char* hay, const char* needle) { - char tokenized_needle[strlen(needle)]; - strcpy(tokenized_needle, needle); - - char* token = strtok(tokenized_needle, " "); - char* str = (char*) hay; - - while (token != NULL) { - if ((str = strcasestr(str, token)) == NULL) return 0; - token = strtok(NULL, " "); - } - - return 1; -} - -uint is_path(const char* path) { - FILE* fp; - if ((fp = fopen(path, "r")) != NULL) { - fclose(fp); - return 1; - } - - return 0; -} diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 3f8f53e..0000000 --- a/src/utils.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#ifndef WAFFY_UTILS_H -#define WAFFY_UTILS_H -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include - -// Read whole file into a string -char* read_file (const char* path); - -// Removes whitespaces from start and end of string -char* str_trim (const char* str); - -// Checks if hay starts with needle -int str_starts_with (const char* hay, const char* needle); - -// Checks if hay ends with needle -int str_ends_with (const char* hay, const char* needle); - -// Checks if hay contains needle substrings (substrings are determined by spaces in needle) -int str_fuzzy_match (const char* hay, const char* needle); - -// Joins two strings together -char* str_concat (const char* string1, const char* string2); - -// Returns relative path to user home -char* get_user_home (); - -// Check if path is real path/exists -uint is_path (const char* path); - -#endif //WAFFY_UTILS_H