pax_global_header00006660000000000000000000000064151732473630014525gustar00rootroot0000000000000052 comment=c682906a0836447c27c8d974f35493d3baa79d64 hyprwm-hyprlauncher-c682906/000077500000000000000000000000001517324736300160405ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/.clang-format000066400000000000000000000034161517324736300204170ustar00rootroot00000000000000--- Language: Cpp BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveMacros: true AlignConsecutiveAssignments: true AlignEscapedNewlines: Right AlignOperands: false AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: true AllowShortCaseLabelsOnASingleLine: true AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: All AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes BreakBeforeBraces: Attach BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon ColumnLimit: 180 CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: false IncludeBlocks: Preserve IndentCaseLabels: true IndentWidth: 4 PointerAlignment: Left ReflowComments: false SortIncludes: false SortUsingDeclarations: false SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Auto TabWidth: 4 UseTab: Never AllowShortEnumsOnASingleLine: false BraceWrapping: AfterEnum: false AlignConsecutiveDeclarations: AcrossEmptyLines NamespaceIndentation: All hyprwm-hyprlauncher-c682906/.clang-tidy000066400000000000000000000072071517324736300201020ustar00rootroot00000000000000WarningsAsErrors: '*' HeaderFilterRegex: '.*\.hpp' FormatStyle: 'file' Checks: > -*, bugprone-*, -bugprone-easily-swappable-parameters, -bugprone-forward-declaration-namespace, -bugprone-forward-declaration-namespace, -bugprone-macro-parentheses, -bugprone-narrowing-conversions, -bugprone-branch-clone, -bugprone-assignment-in-if-condition, concurrency-*, -concurrency-mt-unsafe, cppcoreguidelines-*, -cppcoreguidelines-owning-memory, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-avoid-const-or-ref-data-members, -cppcoreguidelines-non-private-member-variables-in-classes, -cppcoreguidelines-avoid-goto, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-avoid-do-while, -cppcoreguidelines-avoid-non-const-global-variables, -cppcoreguidelines-special-member-functions, -cppcoreguidelines-explicit-virtual-functions, -cppcoreguidelines-avoid-c-arrays, -cppcoreguidelines-pro-bounds-pointer-arithmetic, -cppcoreguidelines-narrowing-conversions, -cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-member-init, -cppcoreguidelines-macro-usage, -cppcoreguidelines-macro-to-enum, -cppcoreguidelines-init-variables, -cppcoreguidelines-pro-type-cstyle-cast, -cppcoreguidelines-pro-type-vararg, -cppcoreguidelines-pro-type-reinterpret-cast, google-global-names-in-headers, -google-readability-casting, google-runtime-operator, misc-*, -misc-unused-parameters, -misc-no-recursion, -misc-non-private-member-variables-in-classes, -misc-include-cleaner, -misc-use-anonymous-namespace, -misc-const-correctness, modernize-*, -modernize-return-braced-init-list, -modernize-use-trailing-return-type, -modernize-use-using, -modernize-use-override, -modernize-avoid-c-arrays, -modernize-macro-to-enum, -modernize-loop-convert, -modernize-use-nodiscard, -modernize-pass-by-value, -modernize-use-auto, performance-*, -performance-avoid-endl, -performance-unnecessary-value-param, portability-std-allocator-const, readability-*, -readability-function-cognitive-complexity, -readability-function-size, -readability-identifier-length, -readability-magic-numbers, -readability-uppercase-literal-suffix, -readability-braces-around-statements, -readability-redundant-access-specifiers, -readability-else-after-return, -readability-container-data-pointer, -readability-implicit-bool-conversion, -readability-avoid-nested-conditional-operator, -readability-redundant-member-init, -readability-redundant-string-init, -readability-avoid-const-params-in-decls, -readability-named-parameter, -readability-convert-member-functions-to-static, -readability-qualified-auto, -readability-make-member-function-const, -readability-isolate-declaration, -readability-inconsistent-declaration-parameter-name, -clang-diagnostic-error, CheckOptions: performance-for-range-copy.WarnOnAllAutoCopies: true performance-inefficient-string-concatenation.StrictMode: true readability-braces-around-statements.ShortStatementLines: 0 readability-identifier-naming.ClassCase: CamelCase readability-identifier-naming.ClassIgnoredRegexp: I.* readability-identifier-naming.ClassPrefix: C # We can't use regex here?!?!?!? readability-identifier-naming.EnumCase: CamelCase readability-identifier-naming.EnumPrefix: e readability-identifier-naming.EnumConstantCase: UPPER_CASE readability-identifier-naming.FunctionCase: camelBack readability-identifier-naming.NamespaceCase: CamelCase readability-identifier-naming.StructPrefix: S readability-identifier-naming.StructCase: CamelCase hyprwm-hyprlauncher-c682906/.github/000077500000000000000000000000001517324736300174005ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/.github/workflows/000077500000000000000000000000001517324736300214355ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/.github/workflows/nix.yml000066400000000000000000000004671517324736300227650ustar00rootroot00000000000000name: Build on: [push, pull_request, workflow_dispatch] jobs: nix: if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork) uses: hyprwm/actions/.github/workflows/nix.yml@main secrets: inherit with: command: nix flake check --print-build-logs --keep-going hyprwm-hyprlauncher-c682906/.gitignore000066400000000000000000000012371517324736300200330ustar00rootroot00000000000000CMakeLists.txt.user CMakeCache.txt CMakeFiles CMakeScripts Testing Makefile cmake_install.cmake install_manifest.txt compile_commands.json CTestTestfile.cmake _deps CMakeUserPresets.json # CLion # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #cmake-build-* build/ .vscode/ .cache/ protocols/*.cpp protocols/*.hpp *.inc src/renderer/gl/shaders/Shaders.hpphyprwm-hyprlauncher-c682906/CMakeLists.txt000066400000000000000000000054171517324736300206070ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.19) file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW) string(STRIP ${VER_RAW} HYPRLAUNCHER_VERSION) add_compile_definitions(HYPRLAUNCHER_VERSION="${HYPRLAUNCHER_VERSION}") project( hyprlauncher VERSION ${HYPRLAUNCHER_VERSION} DESCRIPTION "A multipurpose and versatile launcher / picker for Hyprland") include(CTest) include(CheckIncludeFile) include(GNUInstallDirs) set(PREFIX ${CMAKE_INSTALL_PREFIX}) set(INCLUDE ${CMAKE_INSTALL_FULL_INCLUDEDIR}) set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR}) find_package(PkgConfig REQUIRED) pkg_check_modules( deps REQUIRED IMPORTED_TARGET hyprtoolkit pixman-1 libdrm hyprutils>=0.10.2 hyprwire icu-uc hyprlang fontconfig libqalculate ) set(CMAKE_CXX_STANDARD 23) add_compile_options( -Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) message(STATUS "Configuring hyprlauncher in Debug") add_compile_definitions(HYPRLAUNCHER_DEBUG) else() add_compile_options(-O3) message(STATUS "Configuring hyprlauncher in Release") endif() file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "protocols/*.cpp" "include/*.hpp") add_executable(hyprlauncher ${SRCFILES}) function(hyprprotocol protoPath protoName) set(path ${CMAKE_SOURCE_DIR}/${protoPath}) add_custom_command( OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-client.cpp ${CMAKE_SOURCE_DIR}/protocols/${protoName}-client.hpp ${CMAKE_SOURCE_DIR}/protocols/${protoName}-spec.hpp COMMAND hyprwire-scanner --client ${path}/${protoName}.xml ${CMAKE_SOURCE_DIR}/protocols/ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) target_sources(hyprlauncher PRIVATE protocols/${protoName}-client.cpp protocols/${protoName}-client.hpp protocols/${protoName}-spec.hpp) endfunction() function(hyprprotocolServer protoPath protoName) set(path ${CMAKE_SOURCE_DIR}/${protoPath}) add_custom_command( OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-server.cpp ${CMAKE_SOURCE_DIR}/protocols/${protoName}-server.hpp COMMAND hyprwire-scanner ${path}/${protoName}.xml ${CMAKE_SOURCE_DIR}/protocols/ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) target_sources(hyprlauncher PRIVATE protocols/${protoName}-server.cpp protocols/${protoName}-server.hpp ) endfunction() hyprprotocol(protocols hyprlauncher_core) hyprprotocolServer(protocols hyprlauncher_core) target_include_directories(hyprlauncher PRIVATE "./protocols") target_link_libraries(hyprlauncher PkgConfig::deps) install(TARGETS hyprlauncher)hyprwm-hyprlauncher-c682906/LICENSE000066400000000000000000000027371517324736300170560ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2025, Hypr Development Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. hyprwm-hyprlauncher-c682906/README.md000066400000000000000000000010601517324736300173140ustar00rootroot00000000000000## hyprlauncher A multipurpose and versatile launcher / picker for Hyprland ![](./assets/preview.png) ## Features - Various providers: Desktop, Unicode, Emoji, Math, Font ... - Speedy: Fast, multi-threaded fuzzy searching - Daemon by default: instant opening of the launcher - Entry frequency caching: commonly used entries appear above others - Manual entry providing: make a simple selector from your own list ## Runtime dependencies - Desktop: none - Unicode: `wl-copy` - Math: `wl-copy` for copying the result - Font: `wl-copy` for copying the result hyprwm-hyprlauncher-c682906/VERSION000066400000000000000000000000051517324736300171030ustar00rootroot000000000000000.1.6hyprwm-hyprlauncher-c682906/assets/000077500000000000000000000000001517324736300173425ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/assets/preview.png000066400000000000000000000425301517324736300215350ustar00rootroot00000000000000PNG  IHDR_b IDATxwXWgfЖ,D}=11Cc4KTD5GcM&v=6Ԁec 4a]\gϜ9䝛3.F`xٽxJ*NR2$\.'pRT2r #QXe%,#e%2ruyDS-c=v 5Hܳެj7zwm>omNTϮ$ǾűUk-ۿ/0q̗Gk|֭?8R>#~~Y~'NNj{G#S[vc3?5G?_jѬuVRho_:qԕ$s5o=5HDvͽ몛l(ukM-y'*'|խ#~'L nH8oĹ76-B5nۇ{~S" !!"F AH(#yF8VqFT`Aݔ)чK1 Dd$ F"xNVaODFOdē\=~MaVjF͞(1CZٛo`g^8y^vx׍brČǁIX"Uo.cJĩ9m(dVVUYJtJ'V=!02bӿ D xA02I'zD^0eCZ$7'"v{nŇsebM^;?Ij^V٤m+?b&+ȬA!i8wJzRs!3'"*0ћv 'O7 #DF=/0xa" /!gqaϝOdMO8{Nfnd:OL2DDDvv'Wj:DfCpZBfȹvi"s7v1:UٱiF;VU%CFRWS>]p 5UD@D$s7M]9 άɵʆ#ֲͭ""MJ*߯ae'3]yV^n,7YΚ]bMJj͵^C넰Km󌈴 "҅^S{4ɎsVU׶\hr$^^8%ğKޫJ9mUS@b@D@ 2^IIAX'#q IJ@)/M.:N1&J:k(ڕk{>ÁD G/t9nX545.GOʏ^>p Q\"rκsi7:QGݼJDD.هx#I8!Oo'"^u>i)M'.Z$"B**)GӃC)YURrd,# ,I8cF`L 2 ˲J9T"WXl ^dSZ%Uʅpde"EW*ŦJ\ke9+INN-UM\MՙYM'+Ύy<TFgLˣxgx!!aH`%Lף# җs?q9OYg͗]˥lSSҊ^eȍK-\T!0r%)""^c7 K,C@Ę Ězٗ{" EN+!TJ49 H H$0 'bH1xfG&xSR1(Fqxs 0@Q DA`( ͛GDωUV Whrʏ?xΝ;Ϙ1c4qʔ)oa~PP@90֭[7i$"ZjUzos;v9sS绸,ZH&YfDҿS:u?~ZZZppҥKVXQnݓ'O:ujԨQ...DԾ}wygѢEe?((F<ϧQzzzbbb~y:uL:5""Xꫯm?^y};fjJј2U~z;w6lɓ/_iӦ1c,^x=w\ُ +UR%})-N:C 4hy{{zxxT;whڈݻ,h,YjWYcѣ^P`_#iӦ={R$==ݼ055U"( Fs̙Gկ__~)p K[V Cff&899>|o߾^^^] qqqzHoԹs\޶m󼇇O?dɒ'qgTN/!0AXzujՖ.]ڱc֭[+u pڵ:**^^F2DtҥC}/`*M|6G59O=k.,r8qK./{̝;O K7Q DA`(  0*ζv"C`Tv{ 9Ҏ\窧kʑ#0*n~y4ncjsr[g߰vN 0F_r(٪uWL OwG"rkljǪDJ] (#:EghLUsZy7nڊ;ɒxnq)j'ec{ܳoaۨmbL!L&{v=d2 Eb[YN%$mީ[U>5:n:Ue ^omm]E9&>HGDd+UbY}/8JOk&}CC}9LzcY!|@$y]߅A;#7A#ٽV5RA!ʔIYn5eaر#JC$aȑc֙wz;kܜkVy_ͷTQپ[ΐM dže:m'=HMutnpifpYMn6I6°e1)mSIN#Gwfͫ%$qGSWC2%]j:usU557V i77uX۫%z#Jkd7F;Lk#%]I_K.J^پUKRG>qw6YIVxy) D%).//OL50@RΗq OkW-wR|EV[ږ]V͸F3DytJ"s{k^ =< 9cBP~{Q"q̙/.*J5imPRi`͢"NG^* 6ttVQjrrvJ=<؟]wQQCeP4@ܪ'CzԪ\9~Ǻ5jcz}CDS."w:nɐ7 /ܰ^tt]^}nzo:83#E,k"NJe\./Z:~̽3Need9 h'rŜJj2lL@D/'A㈨].-ZIM]6+p͢ENNsXS^O<;kg"jSwW͟(#0&?]9wC W[wIÉnj}Owl7}փ)4"HO@D,}=ֵk O3U>|1"xK/߲m8=cT,֮4"݋6y֩tuwP: VTTo/jχw|245N7_UJVyr8$J"q"WI`ΎMt̳GѤ=*=U-Hl24SjogX U;Sj"%rFCDNN慎N!;+>?䟇zWӫ PTx(!K$O=EVINLlߥk~\.oѮKFcuOOχ5$%}5e q?]E A~ZJ5Y߯h۹KmGMvn Wy~4m4v2cr[riqwޱ]ӭYnFkE:9ge~7}YJ*'O|_dsa:avSjSG}y֭?_ӫ֊9sF#]xQz;*m~b/ DA`(  0@R0 xA]gWOe vLRQo?xԩ]{N;҅ƌq&"ZdqXDĖM-n5k<ѣ /[u->앿3f޻޾. NOOk}vpp4y{COΟ2mZ~7[ONܼeKVL#God=_"v;~o+@y \>n' B^T)}"W8+?rjZ6{w:y2##Sγ9q( J,{LjʉDl~} uuҥii3f,Ziڵ;~؉G=qxys7KѬ35{iS74 1+Wѡu7;g -2?Ѽs3vh6W[[?0kn.*ծVXGVy^}Ja9s?|֫턉aDrql޼c6ӫ7n᎝:=r$-5e1|sL1U ]dѢ;v `޼~g׮gΔm޴aƍc4}7[Xt˗hуxS#Fhsr(>>~wGNNZ&4uݽ{ŏz޶ui}{Xt:T'{Hmk,QnEcQQO޹Sd(SZQ:u\]] 5XxCOOJmVN,i4_V]b3;6mV˖þb]1cFctTTΝ9ٲe+77j)-~\Fo͝?}:Uv74_5\zX!OD1uW;;ƭ7\%UBUBAD'M: (KjZ"(4MIII 4ع}<Θy˖C\zjMqU+V^X{殉(<,,11O߾:ջOG68Va Xe5%,Qh4Dd^l02·s{w}Fzz[g-Nwߍ0~M nh߿5^}۶. Ƽa)WL= mԱQDصk\ޮ}K/FܸqISMյk۫\]TYYY98"a^߿wNn Lw5PjSOf󯾢 +CBCVܥKv&M{> Wy~ҥM5 n׮}.]}k9:};wvlۦ-XبQO}|DvcaE_e"{4h;<{~5zN |ЫWo4pUVǎ-%ݖR%"E:yrb@3z}Ə_T@Xh&=wW\֭תUaLWu.\8`#F߿>rrv>bĆuN8!ɬڴm;7&6}%7~ݺ4lE9ø h IDAT+(FzZ\̊եfߚ$M|ljr,NJO߾_~٣{kk?Yh={y󪸹 +{Spb)]^*}ׯI'yPfFk* =rHݱ㼧_xty2 ߾ukegǎ-{SKR{EY%DA`( `Y-"qsj;JVR!3!#P0TkpXų¹{,FP@钶ܾ3ܗȈpRN{Rb&72u}f_sY~1gog'ZVһygWD'v}tzѦ^zUԭ~].jFPybӮ 4{vLLz@ȴ iWi,eR :bYVA YsS^4.Itj^5珇sbt xZfhc3ԉ>K KMN5/Q DA`(J'(\W(]\F jKrժU h֬YllU^tٝ;v$<|Xd2R;dvQG ><<= s9sf޻wID;owRWZA^g:qx9*wTUWo޴sWJBQ /ELę .^|ܙ]ݴq%KfΚ[w_JqYmR4հ>> ^:hRRcCgRSʻGi4LaQ׫7*~}ܜm? {ނ||h 'ߨQR(?,99P?ڵkk4έ]FN3v짟}FD ,&w8Zi3׷~֭ڭ۬ٳ̟>NϘ⭷ !atqpph4DTd&MܩsOzοfmm߷o?Uynp[;#11M>sAK%&% 49޽U+W_Uy65l܆Ȑz4t?a(Je\./ZVMݻNԹ9sN?>?8(HR- ^&Zrb@-[_>~CD]v]tijZZ,prrZvmz;v_qqQQo7o\iar#2aQqڻ{Cҵþ{Kت; C~ޱcYiӦOo٪饒wQ-|VVvf1w%0((ilڸѧgEE[6HDקgر:w5WO6M/XHR$ͪ޻ۼDŽe7e?ˌ0fϙctnp W[޷&qcǍ۲ys7nypN9JDi DIJovwS*߮]_O Xh;_0ooݳk3gJ̬L"-W޺ճge>ŋK*/%$;;{A̙3ӷs瞹 H.4 Ì;oO4I"x oV(Mju.U1|6'n{'+r(V[$0֬^偈޹Sd訨3Z:u\]] 5XxCOOJmVN,i4_V]bEiSQvvvɽڷwIjԬq?~56k6stRQF#$'_κ~횧]EZb~FCD...kXf)-HW[U,1GVMiAD# #!9Cgzgj"qM1+p* ">\TKGa*͓6mz:Ν;;m3,\"S;vVmZVq%"TlllfϚu 1y;:ߧsR\\\ȯӰaI'8~\P u˖Ҿ]%x}{۷gϞX=!!AjǏ;=xeW? ܳC%Wo;uĀ_?3g'_/,4 Ξ;WukժŰa܅ ?|Ĉ 3`@tر;v<|@|;. `=q,E~ׯY[[_zu̙js]%xQ#G 1 C|}?>s&O YRRׯ[:?v@`9:}Rd$1nn`sTcn`4F;"NrWk;0lS/_ʪ@BՋW&%*x҂pI DB`(  0@R""aS'6mz)iWeZJ²ʓ|_SC_qlhuڜ1->Sצ= 1x"gx=X)J" 0@hğ1Q DA`(  0@r>|QIӦD<$dDdٲ[<^j ؽw/q{T6jtYWe/Pyǎx"T(VVVZ,+//Ejպuei?UWo޴sJRPԨQ}(g 3g.\8_w`TV9 λ믷Z/%0D˲xC*旄Ç @{_ѪhވwGGG?JMM8hC4[*"*|ݷ@ʛMO2De|1oO>>_=ϯvskWQSl=^2=__u^ѣ׼9˲g""~XѣG%Q6mo`0\|yúuQQ k~oݺuu:鰰!!Ty{{+oGG ~F-\!g} W/_NLJ4hs̽{V̿UEV4;e]IP6)ѝEVI)Je\./~ΝgϙsQAA*jY2pMU6m8 ƒW|rd2''N:yF1MWND]v]tijZZ,prrZvmzLv^x&=}VVy@.ZԸq7Ξ5fɲeDeOAD4o~}~ƍkm{'w5WO6M/XHR|DeŰvVV6o,u.7j9s̟ >x@qY;nܖ͛wlf*qƟw#*{ :xLW^TTUZz_xLPβovwSJo׮/',;z̘ɓ& @Da?aLHD11vlݦ͑Çsrj5SLVwSIZ=bpmNoݾ?(ʅN%g#sT"fj:w)r:u\]] 5/wpt,\gϞ.^LQY#S'N|3n\^,!ĔFwp8~htq*j֭kwz`9ãz)-~}"rZJ;o4Z6ݏ#"#(# }@D3(yż[w +CBCVܥKv&M{>ͫuں48ǎmպJUkͺu]uҵ[-[7WK6kܮ]],{[[ukhaժ&MΞ3UwX|o5{D"zI"r^}μyFs}BB ;4hРkn{^KVX˿$EDN7}L^yǛٳΟ/bjСC}{g۶E>=jhR ݏ+BC0qsxz([n^=r0˲^xqzz鰰֙…[~&N7[g>III^n]:vt1E3s%%@Ņyѕ^K+^ O/ DA`(  0@Q DA`(  0@Q DA`(  0@Q |' !!JÆi%˖)J!CJho]ȗW\bR)0yǎx"T(VVVZVL;ϛ[s.mڶ=|s4"q&̅ ,D>Ufܝ۷ڴ9f(F) L6M*Ϟm0 Bcނ|| {_ڵ5s֮^VOZh~訨g6YVߎ^yzy@aUR {R#Kߩss8~|pPJZL&3^Fݺuk֭E7nqٳf,Y޾ԇPYd1{s8P\eƎe۶Jnܸ;u:z:,;8̄333F#l۹u6G.!Tz 5W_5"wPN:C;8:?쳦͚r{Jۙ[ժm,n,rT M4LDDj֬k~޹9:ccciΎXyv*:]$uJ yyyM1Cٳy/m6nqÆYBʡh؄zHףظq?s4ݩSOs,W>|^C!!s熬\/yw~ϞdÆh4ڜ{4*55KX&M$$$W͸qRΙ ":uĀ_?3g' >~QRaʴi6z劘p~f ֱTw77;X7?m 0@Q DA`(  0@Q DA`(  0@Q DA`( Hʷ>|2> p׮^]tq1lV]lR2ͫo^&k7l0 F,ˁ@&?c^OD ʪ3~qʸy-۷YeiH`8s徃KkpWfͲPY62>3` P`GѼM\]]%zܔoz.ް=F q&2>J =u[sO:x{]F9k6wpp˶ڹ0 cp`ys٢cjQ>!KQ\\\Tq+& w܈۶ը,JJapV*ry;u<{ΜǏ RTBd%lF&3faJ":t~2ZNsg:~E*l)(0͝ۯOGv޾;hr/OɫREF3dnprql޼c6Sɍ7<|cNG)rlbҥW._&۷ԻwGMF!۷w99jԩ qӡ jx#>E!'ƚի޽suquu7׼ѱMFctTTΝ9ٲe+77j)-~\F _"q"WIIPIDDwSnܼyCz>//oՊaEX8@2z7o>vWo^hR ODB"*U`x訨 dddXw?&ƘJ~Pڝzf=\_*"&$$|Իoj0%rttLOO/a5onggruMJL,""ec'ń(̎Iqĝ#դaH ;7d_%//ϻc{ؿ$va{=goND<ϧG^z5o4A>ʺ[vJ}' ]jlc$^{9uĀ_?3g'_BZ89;1búu'OdVVVmڶ{`߾~ݺ4l}(Fӌm;XSӧo_/ѽ{~G,Yh=/c"Nl/_ ԜvkYI]Œ<߻O*U233mll7h: =WH'v!%G |%GD7T^=;;÷o݊R%^!0?.s[B{ DA`( ( 'm"02*SUibj_I.U@VM޲QJIFDa@y1f{wFg7Qnn`}do W߇'vd,XV %3HT9 { {HY!ǔGP!q b󢔁[# ׄ-D'F#pϮyKIzR!%RJ$Q|RHyj2xUIxd @c%7+c4&O/zλa$oX˯~'(\WlpyHeİaZ17`QGl\~ӏ?M7kYTV5zu'n[ֲ[e BSD 3-Clause License Copyright (c) 2025, Hypr Development Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This is the core manager object for hyprlauncher operations Sets the open state of the launcher. 0 - toggle 1 - open 2 - close Opens the launcher with given options. If the launcher is already open, this is a no-op. Gets the launcher info object. That object will return some data about the launcher's state Destroys this object. Children remain alive until destroyed. Info object, will emit various information about the state of the launcher Emitted on creation of this object, and when the state changes. Emitted on selection by the user. The launcher will close immediately after this is emitted. Destroys this object. Children remain alive until destroyed. hyprwm-hyprlauncher-c682906/src/000077500000000000000000000000001517324736300166275ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/config/000077500000000000000000000000001517324736300200745ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/config/ConfigManager.cpp000066400000000000000000000052421517324736300233030ustar00rootroot00000000000000#include "ConfigManager.hpp" #include "../helpers/Log.hpp" #include #include #include CConfigManager::CConfigManager() : m_inotifyFd(inotify_init()) { const auto CFGPATH = Hyprutils::Path::findConfig("hyprlauncher").first.value_or(""); m_configPath = CFGPATH; m_config = makeUnique(CFGPATH.c_str(), Hyprlang::SConfigOptions{.allowMissingConfig = true}); m_config->addConfigValue("general:grab_focus", Hyprlang::INT{1}); m_config->addConfigValue("locale:override", Hyprlang::STRING{""}); m_config->addConfigValue("cache:enabled", Hyprlang::INT{1}); m_config->addConfigValue("finders:default_finder", Hyprlang::STRING{"desktop"}); m_config->addConfigValue("finders:desktop_prefix", Hyprlang::STRING{""}); m_config->addConfigValue("finders:unicode_prefix", Hyprlang::STRING{"."}); m_config->addConfigValue("finders:math_prefix", Hyprlang::STRING{"="}); m_config->addConfigValue("finders:font_prefix", Hyprlang::STRING{"'"}); m_config->addConfigValue("finders:desktop_launch_prefix", Hyprlang::STRING{""}); m_config->addConfigValue("finders:desktop_terminal", Hyprlang::STRING{""}); m_config->addConfigValue("finders:desktop_icons", Hyprlang::INT{1}); m_config->addConfigValue("ui:window_size", Hyprlang::VEC2{400, 260}); m_config->commence(); replantWatch(); } void CConfigManager::replantWatch() { for (const auto& w : m_watches) { inotify_rm_watch(m_inotifyFd.get(), w); } m_watches.clear(); m_watches.emplace_back(inotify_add_watch(m_inotifyFd.get(), m_configPath.c_str(), IN_MODIFY | IN_DONT_FOLLOW)); } void CConfigManager::parse() { const auto ERROR = m_config->parse(); if (ERROR.error) Debug::log(ERR, "Error in config: {}", ERROR.getError()); } void CConfigManager::onInotifyEvent() { constexpr size_t BUFFER_SIZE = sizeof(inotify_event) + NAME_MAX + 1; alignas(inotify_event) std::array buffer = {}; const ssize_t bytesRead = read(m_inotifyFd.get(), buffer.data(), buffer.size()); if (bytesRead <= 0) return; for (size_t offset = 0; offset < sc(bytesRead);) { const auto* ev = rc(buffer.data() + offset); if (offset + sizeof(inotify_event) > sc(bytesRead)) { // err break; } if (offset + sizeof(inotify_event) + ev->len > sc(bytesRead)) { // err break; } offset += sizeof(inotify_event) + ev->len; } replantWatch(); parse(); } hyprwm-hyprlauncher-c682906/src/config/ConfigManager.hpp000066400000000000000000000011311517324736300233010ustar00rootroot00000000000000#pragma once #include #include #include "../helpers/Memory.hpp" #include #include class CConfigManager { public: CConfigManager(); void parse(); void onInotifyEvent(); UP m_config; Hyprutils::OS::CFileDescriptor m_inotifyFd; std::vector m_watches; std::string m_configPath; void replantWatch(); }; inline UP g_configManager; hyprwm-hyprlauncher-c682906/src/finders/000077500000000000000000000000001517324736300202615ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/finders/Cache.cpp000066400000000000000000000043421517324736300217730ustar00rootroot00000000000000#include "Cache.hpp" #include "../config/ConfigManager.hpp" #include #include #include #include #include #include using namespace Hyprutils::String; using namespace Hyprutils::Memory; constexpr const size_t CACHE_MAX_SIZE = 512; CEntryCache::CEntryCache(const std::string& name) { std::filesystem::path cacheDirectory; if (const auto XDG_DATA_HOME = getenv("XDG_DATA_HOME"); XDG_DATA_HOME && XDG_DATA_HOME[0] != '\0') cacheDirectory = std::filesystem::path{XDG_DATA_HOME} / "hyprlauncher"; else if (const auto HOME = getenv("HOME")) cacheDirectory = std::filesystem::path{HOME} / ".local" / "share" / "hyprlauncher"; else return; std::error_code ec; if (!std::filesystem::is_directory(cacheDirectory, ec) || ec) std::filesystem::create_directories(cacheDirectory); m_cacheFullPath = cacheDirectory / (name + ".cache"); // load cache std::ifstream ifs(m_cacheFullPath); if (!ifs.good()) return; std::string line; while (std::getline(ifs, line)) { m_cache[line].value += 1; m_cacheStrings.emplace_back(line); } m_good = true; } bool CEntryCache::good() { return m_good; } uint32_t CEntryCache::getCachedEntry(const std::string& entry) { try { return m_cache.at(entry).value; } catch (...) { ; } return 0; } void CEntryCache::incrementCachedEntry(const std::string& entry) { auto& e = m_cache[entry]; ++e.value; m_cacheStrings.emplace_back(entry); save(); } void CEntryCache::trimCache() { if (m_cacheStrings.size() <= CACHE_MAX_SIZE) return; const auto TO_REMOVE = m_cacheStrings.size() - CACHE_MAX_SIZE; m_cacheStrings = std::vector{m_cacheStrings.begin() + TO_REMOVE, m_cacheStrings.end()}; } void CEntryCache::save() { static auto PENABLECACHE = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "cache:enabled"); if (!*PENABLECACHE) return; trimCache(); std::ofstream ofs(m_cacheFullPath); for (const auto& s : m_cacheStrings) { ofs << std::format("{}\n", s); } } hyprwm-hyprlauncher-c682906/src/finders/Cache.hpp000066400000000000000000000012601517324736300217740ustar00rootroot00000000000000#pragma once #include #include #include #include #include class CEntryCache { public: CEntryCache(const std::string& name); ~CEntryCache() = default; bool good(); uint32_t getCachedEntry(const std::string& entry); void incrementCachedEntry(const std::string& entry); private: void trimCache(); void save(); bool m_good = false; struct SCacheEntry { uint32_t value = 0; }; std::unordered_map m_cache; std::vector m_cacheStrings; std::filesystem::path m_cacheFullPath; }; hyprwm-hyprlauncher-c682906/src/finders/FinderTypes.hpp000066400000000000000000000002721517324736300232270ustar00rootroot00000000000000#pragma once #include enum eFinderTypes : uint8_t { FINDER_DESKTOP = 0, FINDER_UNICODE = 1, FINDER_MATH = 2, FINDER_IPC = 3, FINDER_FONT = 4, };hyprwm-hyprlauncher-c682906/src/finders/Fuzzy.cpp000066400000000000000000000176411517324736300221250ustar00rootroot00000000000000#include "Fuzzy.hpp" #include #include #include #include #include #include using namespace Hyprutils::String; static float jaroWinkler(const std::string_view& query, const std::string_view& test) { const auto LENGTH_A = query.length(); const auto LENGTH_B = test.length(); if (!LENGTH_A && !LENGTH_B) return 0; const auto MATCH_DISTANCE = LENGTH_A == 1 && LENGTH_B == 1 ? 0 : ((std::max(LENGTH_A, LENGTH_B) / 2) - 1); std::vector matchesA, matchesB; matchesA.resize(LENGTH_A); matchesB.resize(LENGTH_B); size_t matches = 0; for (size_t i = 0; i < LENGTH_A; ++i) { const size_t start = (i > MATCH_DISTANCE ? i - MATCH_DISTANCE : 0); const size_t end = std::min(i + MATCH_DISTANCE + 1, LENGTH_B); for (size_t j = start; j < end; ++j) { if (matchesB[j] || query[i] != test[j]) continue; matchesA[i] = true; matchesB[j] = true; ++matches; break; } } if (!matches) return 0.F; float t = 0.F; size_t k = 0; for (size_t i = 0; i < LENGTH_A; ++i) { if (!matchesA[i]) continue; while (k < matchesB.size() && !matchesB[k]) { ++k; } if (query[i] != test[k]) t += 0.5F; ++k; } float jaro = (sc(matches) / LENGTH_A + sc(matches) / LENGTH_B + (matches - t) / sc(matches)) / 3.F; // winkler prefix bonus size_t prefixLen = 0; size_t maxPrefix = std::min({LENGTH_A, LENGTH_B, sc(4)}); for (size_t i = 0; i < maxPrefix; ++i) { if (query[i] == test[i]) ++prefixLen; else break; } return jaro + (prefixLen * 0.1F * (1.0F - jaro)); } // constexpr float MIN_FUZZY_TO_COUNT = 0.75F; constexpr float MIN_SALIENT_MATCH = 0.3F; constexpr float MIN_TOKEN_MATCH = 0.15F; constexpr float POPULARITY_FACTOR = 0.08F; constexpr float NO_SALIENT_PENALTY = 0.01F; // static float tokenBestMatch(std::string_view qt, std::string_view lastQ, const std::unordered_set& cset, const std::vector& cTok) { if (qt.empty()) return 0.F; if (cset.contains(qt)) return 1.F; float best = 0.F; bool hasExplicitMatch = false; // prefix or substring match for (auto ct : cTok) { // strong prefix match - especially important for the last token (partial typing) if (ct.starts_with(qt)) { hasExplicitMatch = true; if (!lastQ.empty() && qt == lastQ) { // last token prefix match - user is still typing best = std::max(best, 0.95F); } else best = std::max(best, 0.98F); } else if (ct.contains(qt)) { hasExplicitMatch = true; best = std::max(best, 0.7F); } best = std::max(best, jaroWinkler(qt, ct)); } // if we have an explicit match (prefix/substring), return the score directly if (hasExplicitMatch) return best; // for pure fuzzy matches, require minimum quality if (best < MIN_FUZZY_TO_COUNT) return 0.F; return (best - MIN_FUZZY_TO_COUNT) / (1.F - MIN_FUZZY_TO_COUNT); } static float scoreCandidate(std::string_view query, std::string_view cand, float freq) { CVarList2 qTokens(std::string{query}, 0, 's', true, false); CVarList2 cTokens(std::string{cand}, 0, 's', true, false); std::vector qTok, cTok; qTok.reserve(qTokens.size()); cTok.reserve(cTokens.size()); for (const auto& q : qTokens) { qTok.emplace_back(q); } for (const auto& c : cTokens) { cTok.emplace_back(c); } if (qTok.empty() || cTok.empty()) return 0.F; std::unordered_set cset; cset.reserve(cTok.size()); for (auto t : cTok) { cset.insert(t); } std::string_view lastQ = qTok.back(); // pick salient token as longest std::string_view salient = qTok[0]; for (auto t : qTok) { if (t.size() > salient.size()) salient = t; } float sum = 0.F; float minMatch = 1.F; for (auto qt : qTok) { float match = tokenBestMatch(qt, lastQ, cset, cTok); sum += match; minMatch = std::min(minMatch, match); } // if ANY token matches poorly, penalize heavily if (minMatch < MIN_TOKEN_MATCH) return 0.F; float base = sum / sc(qTok.size()); // normalize it // if salient token doesn't match strongly, kill the score float salientMatch = tokenBestMatch(salient, lastQ, cset, cTok); if (salientMatch < MIN_SALIENT_MATCH) base *= NO_SALIENT_PENALTY; float lenDiff = float(std::abs(int(query.size()) - int(cand.size()))); float lenFactor = std::exp(-lenDiff / 25.f); float popFactor = 1.F + (POPULARITY_FACTOR * std::log1p(std::max(0.F, freq))); return base * lenFactor * popFactor; } struct SScoreData { float score = 0.F; SP result; size_t idx = 0; }; static void workerFn(std::vector& scores, const std::vector>& in, const std::string& query, size_t start, size_t end) { for (size_t i = start; i < end; ++i) { auto& ref = scores[i]; ref.score = scoreCandidate(query, in[i]->fuzzable(), in[i]->frequency()); ref.result = in[i]; ref.idx = i; } } static std::vector> getBestResultsStable(std::vector& data, size_t n) { std::vector> resVec; resVec.resize(std::min(data.size(), n)); static auto getBestResult = [](std::vector& data) -> typename std::vector::iterator { typename std::vector::iterator result = data.begin(); float bestScore = -1.F; for (auto it = data.begin(); it != data.end(); ++it) { if (it->score > bestScore && it->score >= 0.F) { bestScore = it->score; result = it; } } return result; }; for (size_t i = 0; i < std::min(data.size(), n); ++i) { auto it = getBestResult(data); it->score = -1.F; // reset, don't get it again resVec[i] = it->result; } return resVec; } static constexpr const decltype(sysconf(0)) MAX_THREADS = 10; // std::vector> Fuzzy::getNResults(const std::vector>& in, const std::string& query, size_t results) { std::vector scores; scores.resize(in.size()); if (in.size() > 100) { // If we have more than 100 elements, we can run this in threads. // For smaller sets this doesn't make much sense // Value 100 was picked because I felt like it's a good one™. auto THREADS = sysconf(_SC_NPROCESSORS_ONLN); if (THREADS < 1) THREADS = 8; THREADS = std::min(THREADS, MAX_THREADS); std::vector workerThreads; workerThreads.resize(THREADS); size_t workElDone = 0, workElPerThread = in.size() / THREADS; for (long i = 0; i < THREADS; ++i) { if (i == THREADS - 1) { workerThreads[i] = std::thread([&, begin = workElDone] { workerFn(scores, in, query, begin, in.size()); }); break; } workerThreads[i] = std::thread([&, begin = workElDone, end = workElDone + workElPerThread] { workerFn(scores, in, query, begin, end); }); workElDone += workElPerThread; } for (auto& t : workerThreads) { if (t.joinable()) t.join(); } workerThreads.clear(); } else workerFn(scores, in, query, 0, in.size()); return getBestResultsStable(scores, results); }hyprwm-hyprlauncher-c682906/src/finders/Fuzzy.hpp000066400000000000000000000004121517324736300221160ustar00rootroot00000000000000#pragma once #include #include #include "IFinderResult.hpp" #include "../helpers/Memory.hpp" namespace Fuzzy { std::vector> getNResults(const std::vector>& in, const std::string& query, size_t results); };hyprwm-hyprlauncher-c682906/src/finders/IFinder.cpp000066400000000000000000000000671517324736300223100ustar00rootroot00000000000000#include "IFinder.hpp" void IFinder::init() { ; } hyprwm-hyprlauncher-c682906/src/finders/IFinder.hpp000066400000000000000000000012241517324736300223110ustar00rootroot00000000000000#pragma once #include "../helpers/Memory.hpp" #include "IFinderResult.hpp" #include #include #include struct SFinderResult { std::string label; std::string icon; SP result; std::optional overrideFont; bool hasIcon = false; }; constexpr const size_t MAX_RESULTS_PER_FINDER = 50; class IFinder { public: virtual ~IFinder() = default; virtual std::vector getResultsForQuery(const std::string& query) = 0; virtual void init(); protected: IFinder() = default; };hyprwm-hyprlauncher-c682906/src/finders/IFinderResult.cpp000066400000000000000000000001241517324736300235010ustar00rootroot00000000000000#include "IFinderResult.hpp" uint32_t IFinderResult::frequency() { return 0; } hyprwm-hyprlauncher-c682906/src/finders/IFinderResult.hpp000066400000000000000000000006341517324736300235140ustar00rootroot00000000000000#pragma once #include #include "FinderTypes.hpp" class IFinderResult { public: virtual ~IFinderResult() = default; virtual eFinderTypes type() = 0; virtual void run() = 0; virtual const std::string& fuzzable() = 0; virtual const std::string& name() = 0; virtual uint32_t frequency(); protected: IFinderResult() = default; };hyprwm-hyprlauncher-c682906/src/finders/desktop/000077500000000000000000000000001517324736300217325ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/finders/desktop/DesktopFinder.cpp000066400000000000000000000234351517324736300252060ustar00rootroot00000000000000#include "DesktopFinder.hpp" #include "../../helpers/Log.hpp" #include "../Fuzzy.hpp" #include "../Cache.hpp" #include "../../config/ConfigManager.hpp" #include #include #include #include #include #include #include #include #include #include using namespace Hyprutils::String; using namespace Hyprutils::OS; static std::optional readFileAsString(const std::filesystem::path& path) { std::error_code ec; if (!std::filesystem::exists(path, ec) || ec) return std::nullopt; std::ifstream file(path.string()); if (!file.good()) return std::nullopt; return trim(std::string((std::istreambuf_iterator(file)), (std::istreambuf_iterator()))); } class CDesktopEntry : public IFinderResult { public: CDesktopEntry() = default; virtual ~CDesktopEntry() = default; virtual const std::string& fuzzable() { return m_fuzzable; } virtual eFinderTypes type() { return FINDER_DESKTOP; } virtual uint32_t frequency() { return m_frequency; } virtual const std::string& name() { return m_name; } virtual void run() { static auto PLAUNCHPREFIX = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "finders:desktop_launch_prefix"); static auto PTERMINALEXEC = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "finders:desktop_terminal"); const std::string_view LAUNCH_PREFIX = *PLAUNCHPREFIX; const std::string_view TERMINAL_EXEC = *PTERMINALEXEC; auto toExec = std::format("{}{}{}", LAUNCH_PREFIX.empty() ? std::string{""} : std::string{LAUNCH_PREFIX} + std::string{" "}, m_terminal && !TERMINAL_EXEC.empty() ? std::string{TERMINAL_EXEC} + std::string{" "} : std::string{""}, m_exec); Debug::log(TRACE, "Running {}", toExec); g_desktopFinder->m_entryFrequencyCache->incrementCachedEntry(m_fuzzable); m_frequency = g_desktopFinder->m_entryFrequencyCache->getCachedEntry(m_fuzzable); // replace all funky codes with nothing replaceInString(toExec, "%U", ""); replaceInString(toExec, "%f", ""); replaceInString(toExec, "%F", ""); replaceInString(toExec, "%u", ""); replaceInString(toExec, "%i", ""); replaceInString(toExec, "%c", ""); replaceInString(toExec, "%k", ""); replaceInString(toExec, "%d", ""); replaceInString(toExec, "%D", ""); replaceInString(toExec, "%N", ""); replaceInString(toExec, "%n", ""); CProcess proc("/bin/sh", {"-c", toExec}); proc.runAsync(); } std::string m_name, m_exec, m_icon, m_fuzzable, m_stem; bool m_terminal = false; uint32_t m_frequency = 0; }; static std::filesystem::path resolvePath(const std::string& p) { if (p[0] != '~') return p; const auto HOME = getenv("HOME"); if (!HOME) return ""; return std::filesystem::path(HOME) / p.substr(2); } CDesktopFinder::CDesktopFinder() : m_inotifyFd(inotify_init()), m_entryFrequencyCache(makeUnique("desktop")) { if (const auto DATA_HOME = getenv("XDG_DATA_HOME")) m_envPaths.emplace_back(std::filesystem::path(DATA_HOME) / "applications"); else m_envPaths.emplace_back(resolvePath("~/.local/share/applications")); if (const auto DATA_DIRS = getenv("XDG_DATA_DIRS")) { CConstVarList paths(DATA_DIRS, 0, ':', false); for (const auto& p : paths) m_envPaths.emplace_back(std::filesystem::path(p) / "applications"); } else { m_envPaths.emplace_back("/usr/local/share/applications"); m_envPaths.emplace_back("/usr/share/applications"); } } void CDesktopFinder::init() { recache(); replantWatch(); } void CDesktopFinder::onInotifyEvent() { recache(); replantWatch(); } void CDesktopFinder::recache() { m_desktopEntryPaths.clear(); m_desktopEntryCache.clear(); m_desktopEntryCacheGeneric.clear(); std::unordered_set desktopFileIds; std::unordered_set directories; std::function cacheDirectory; cacheDirectory = [this, &cacheDirectory, &desktopFileIds, &directories](const std::filesystem::path& base, const std::filesystem::path& p) { std::error_code ec; auto canonicalPath = std::filesystem::canonical(p, ec); if (ec || !directories.insert(canonicalPath).second) { Debug::log(TRACE, "desktop: skipping {}, does not exist / already visited", p.string()); return; } auto it = std::filesystem::directory_iterator(p, ec); if (ec) return; for (const auto& e : it) { auto status = e.status(ec); if (ec) continue; if (std::filesystem::is_regular_file(status)) { auto relDesktopFilePath = e.path().lexically_relative(base); if (relDesktopFilePath.extension() != ".desktop") { Debug::log(TRACE, "desktop: skipping non-desktop file at {}", e.path().string()); continue; } auto desktopFileId = relDesktopFilePath.string(); std::ranges::replace(desktopFileId, '/', '-'); if (desktopFileIds.insert(desktopFileId).second) cacheEntry(e.path()); else Debug::log(TRACE, "desktop: skipping entry at {}, already cached desktopFileId {}", e.path().string(), desktopFileId); } else if (std::filesystem::is_directory(status)) cacheDirectory(base, e.path()); } m_desktopEntryPaths.emplace_back(p); }; for (const auto& PATH : m_envPaths) { cacheDirectory(PATH, PATH); } } void CDesktopFinder::replantWatch() { for (const auto& w : m_watches) { inotify_rm_watch(m_inotifyFd.get(), w); } m_watches.clear(); while (true) { pollfd pfd = { .fd = m_inotifyFd.get(), .events = POLLIN, }; poll(&pfd, 1, 0); if (!(pfd.revents & POLLIN)) break; static char buf[1024]; read(m_inotifyFd.get(), buf, 1023); } for (const auto& p : m_desktopEntryPaths) { m_watches.emplace_back(inotify_add_watch(m_inotifyFd.get(), p.c_str(), IN_MODIFY | IN_DONT_FOLLOW | IN_CREATE | IN_DELETE | IN_MOVE)); } } void CDesktopFinder::cacheEntry(const std::filesystem::path& path) { Debug::log(TRACE, "desktop: caching entry at {}", path.string()); const auto READ_RESULT = readFileAsString(path); if (!READ_RESULT) return; const auto& DATA = *READ_RESULT; auto extract = [&DATA](const std::string_view what) -> std::string_view { size_t begins = DATA.find("\n" + std::string{what} + " "); if (begins == std::string::npos) begins = DATA.find("\n" + std::string{what} + "="); if (begins == std::string::npos) return ""; begins = DATA.find('=', begins); if (begins == std::string::npos) return ""; begins += 1; // eat the equals while (begins < DATA.size() && std::isspace(DATA[begins])) { ++begins; } size_t ends = DATA.find("\n", begins + 1); if (!ends) return std::string_view{DATA}.substr(begins); return std::string_view{DATA}.substr(begins, ends - begins); }; const auto NAME = extract("Name"); const auto ICON = extract("Icon"); const auto EXEC = extract("Exec"); const auto NODISPLAY = extract("NoDisplay") == "true"; const auto TERMINAL = extract("Terminal") == "true"; if (EXEC.empty() || NAME.empty() || NODISPLAY) { Debug::log(TRACE, "desktop: skipping entry, empty name / exec / NoDisplay"); return; } auto pathStem = path.stem().string(); if (path.string().starts_with("/home")) { // home paths should override system ones std::erase_if(m_desktopEntryCache, [&pathStem](const auto& e) { return e->m_stem == pathStem; }); } auto& e = m_desktopEntryCache.emplace_back(makeShared()); e->m_exec = EXEC; e->m_icon = ICON; e->m_name = NAME; e->m_fuzzable = NAME; e->m_stem = std::move(pathStem); e->m_terminal = TERMINAL; std::ranges::transform(e->m_fuzzable, e->m_fuzzable.begin(), ::tolower); e->m_frequency = m_entryFrequencyCache->getCachedEntry(e->m_fuzzable); m_desktopEntryCacheGeneric.emplace_back(e); Debug::log(TRACE, "desktop: cached {} with icon {} and exec line of \"{}\"", NAME, ICON, EXEC); } std::vector CDesktopFinder::getResultsForQuery(const std::string& query) { static auto PICONSENABLED = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "finders:desktop_icons"); std::vector results; auto fuzzed = Fuzzy::getNResults(m_desktopEntryCacheGeneric, query, MAX_RESULTS_PER_FINDER); results.reserve(fuzzed.size()); for (const auto& f : fuzzed) { const auto p = reinterpretPointerCast(f); if (!p) continue; results.emplace_back(SFinderResult{ .label = p->m_name, .icon = *PICONSENABLED ? p->m_icon : "", .result = p, .hasIcon = true, }); } return results; } hyprwm-hyprlauncher-c682906/src/finders/desktop/DesktopFinder.hpp000066400000000000000000000022151517324736300252040ustar00rootroot00000000000000#pragma once #include "../IFinder.hpp" #include #include #include class CDesktopEntry; class CEntryCache; class CDesktopFinder : public IFinder { public: CDesktopFinder(); virtual ~CDesktopFinder() = default; virtual std::vector getResultsForQuery(const std::string& query); virtual void init(); Hyprutils::OS::CFileDescriptor m_inotifyFd; void onInotifyEvent(); private: std::vector> m_desktopEntryCache; std::vector> m_desktopEntryCacheGeneric; std::vector m_desktopEntryPaths; std::vector m_watches; std::vector m_envPaths; UP m_entryFrequencyCache; void cacheEntry(const std::filesystem::path& path); void replantWatch(); void recache(); friend class CDesktopEntry; }; inline UP g_desktopFinder; hyprwm-hyprlauncher-c682906/src/finders/font/000077500000000000000000000000001517324736300212275ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/finders/font/FontFinder.cpp000066400000000000000000000057131517324736300237770ustar00rootroot00000000000000#include "FontFinder.hpp" #include "../../helpers/Log.hpp" #include "../Fuzzy.hpp" #include #include #include using namespace Hyprutils::OS; class CFontEntry : public IFinderResult { public: CFontEntry() = default; virtual ~CFontEntry() = default; virtual const std::string& fuzzable() { return m_fuzzable; } virtual eFinderTypes type() { return FINDER_MATH; } virtual const std::string& name() { return m_font; } virtual void run() { Debug::log(TRACE, "Copying {} with wl-copy", m_font); CProcess proc("wl-copy", {m_font}); proc.runAsync(); } std::string m_font, m_fuzzable; }; CFontFinder::CFontFinder() : m_valid(FcInit()) { if (!m_valid) Debug::log(ERR, "font: FcInit failed, fonts unavailable"); refreshFonts(); } CFontFinder::~CFontFinder() { FcFini(); } void CFontFinder::refreshFonts() { if (!m_valid) return; m_entries.clear(); m_entriesGeneric.clear(); FcPattern* pattern = FcPatternCreate(); FcObjectSet* objectSet = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, nullptr); FcFontSet* fontSet = FcFontList(nullptr, pattern, objectSet); if (fontSet) { for (int i = 0; i < fontSet->nfont; i++) { FcPattern* font = fontSet->fonts[i]; FcChar8 * family = nullptr, *style = nullptr; if (FcPatternGetString(font, FC_FAMILY, 0, &family) != FcResultMatch || FcPatternGetString(font, FC_STYLE, 0, &style) != FcResultMatch) continue; if (!family || !style) continue; auto e = m_entries.emplace_back(makeShared()); e->m_font = std::format("{} {}", (char*)family, (char*)style); e->m_fuzzable = e->m_font; std::ranges::transform(e->m_fuzzable, e->m_fuzzable.begin(), ::tolower); m_entriesGeneric.emplace_back(std::move(e)); } } FcFontSetDestroy(fontSet); FcObjectSetDestroy(objectSet); FcPatternDestroy(pattern); } std::vector CFontFinder::getResultsForQuery(const std::string& query) { std::vector results; std::vector> fuzzed; if (!query.empty()) fuzzed = Fuzzy::getNResults(m_entriesGeneric, query, MAX_RESULTS_PER_FINDER); else fuzzed = std::vector>{m_entriesGeneric.begin(), m_entriesGeneric.begin() + std::min(m_entriesGeneric.size(), MAX_RESULTS_PER_FINDER)}; results.reserve(fuzzed.size()); for (const auto& f : fuzzed) { const auto p = reinterpretPointerCast(f); if (!p) continue; results.emplace_back(SFinderResult{ .label = p->m_font, .icon = "", .result = p, .overrideFont = p->m_font, }); } return results; } hyprwm-hyprlauncher-c682906/src/finders/font/FontFinder.hpp000066400000000000000000000007611517324736300240020ustar00rootroot00000000000000#pragma once #include "../IFinder.hpp" class CFontEntry; class CFontFinder : public IFinder { public: CFontFinder(); virtual ~CFontFinder(); virtual std::vector getResultsForQuery(const std::string& query); private: std::vector> m_entries; std::vector> m_entriesGeneric; void refreshFonts(); bool m_valid = false; }; inline UP g_fontFinder; hyprwm-hyprlauncher-c682906/src/finders/ipc/000077500000000000000000000000001517324736300210345ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/finders/ipc/IPCFinder.cpp000066400000000000000000000047771517324736300233220ustar00rootroot00000000000000#include "IPCFinder.hpp" #include "../../helpers/Log.hpp" #include "../Fuzzy.hpp" #include "../Cache.hpp" #include #include #include #include #include #include #include using namespace Hyprutils::String; using namespace Hyprutils::OS; class CIPCEntry : public IFinderResult { public: CIPCEntry() = default; virtual ~CIPCEntry() = default; virtual const std::string& fuzzable() { return m_fuzzable; } virtual eFinderTypes type() { return FINDER_IPC; } virtual uint32_t frequency() { return 0; } virtual const std::string& name() { return m_entry; } virtual void run() { Debug::log(TRACE, "Selected {}", m_entry); } std::string m_entry, m_fuzzable; }; CIPCFinder::CIPCFinder() = default; void CIPCFinder::init() { ; } void CIPCFinder::setData(const std::vector& data) { m_entries.clear(); m_entriesGeneric.clear(); for (const auto& s : data) { auto e = m_entries.emplace_back(makeShared()); m_entriesGeneric.emplace_back(e); e->m_entry = s; e->m_fuzzable = s; std::ranges::transform(e->m_fuzzable, e->m_fuzzable.begin(), ::tolower); } } void CIPCFinder::setData(const std::vector& data) { m_entries.clear(); m_entriesGeneric.clear(); for (const auto& s : data) { auto e = m_entries.emplace_back(makeShared()); m_entriesGeneric.emplace_back(e); e->m_entry = s; e->m_fuzzable = s; std::ranges::transform(e->m_fuzzable, e->m_fuzzable.begin(), ::tolower); } } std::vector CIPCFinder::getResultsForQuery(const std::string& query) { std::vector results; std::vector> fuzzed; if (!query.empty()) fuzzed = Fuzzy::getNResults(m_entriesGeneric, query, MAX_RESULTS_PER_FINDER); else fuzzed = std::vector>{m_entriesGeneric.begin(), m_entriesGeneric.begin() + std::min(m_entriesGeneric.size(), MAX_RESULTS_PER_FINDER)}; results.reserve(fuzzed.size()); for (const auto& f : fuzzed) { const auto p = reinterpretPointerCast(f); if (!p) continue; results.emplace_back(SFinderResult{ .label = p->m_entry, .icon = "", .result = p, }); } return results; } hyprwm-hyprlauncher-c682906/src/finders/ipc/IPCFinder.hpp000066400000000000000000000012611517324736300233100ustar00rootroot00000000000000#pragma once #include "../IFinder.hpp" #include class CIPCEntry; class CIPCFinder : public IFinder { public: CIPCFinder(); virtual ~CIPCFinder() = default; virtual std::vector getResultsForQuery(const std::string& query); virtual void init(); void setData(const std::vector& data); void setData(const std::vector& data); private: std::vector> m_entries; std::vector> m_entriesGeneric; friend class CIPCEntry; }; inline UP g_ipcFinder; hyprwm-hyprlauncher-c682906/src/finders/math/000077500000000000000000000000001517324736300212125ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/finders/math/MathFinder.cpp000066400000000000000000000025501517324736300237410ustar00rootroot00000000000000#include "MathFinder.hpp" #include "../../helpers/Log.hpp" #include #include using namespace Hyprutils::OS; static UP qalculator; class CMathEntry : public IFinderResult { public: CMathEntry() = default; virtual ~CMathEntry() = default; virtual const std::string& fuzzable() { return m_fuzzable; } virtual eFinderTypes type() { return FINDER_MATH; } virtual const std::string& name() { return m_result; } virtual void run() { Debug::log(TRACE, "Copying {} with wl-copy", m_result); CProcess proc("wl-copy", {m_result}); proc.runAsync(); } std::string m_expr, m_result, m_fuzzable; }; CMathFinder::CMathFinder() = default; std::vector CMathFinder::getResultsForQuery(const std::string& query) { if (!qalculator) { qalculator = makeUnique(); qalculator->loadExchangeRates(); qalculator->loadGlobalDefinitions(); qalculator->loadLocalDefinitions(); } SFinderResult result; auto entry = makeShared(); entry->m_expr = query; entry->m_result = qalculator->calculateAndPrint(query, 1500); result.result = entry; result.label = "= " + entry->m_result; return {result}; } hyprwm-hyprlauncher-c682906/src/finders/math/MathFinder.hpp000066400000000000000000000004431517324736300237450ustar00rootroot00000000000000#pragma once #include "../IFinder.hpp" class CMathEntry; class CMathFinder : public IFinder { public: CMathFinder(); virtual ~CMathFinder() = default; virtual std::vector getResultsForQuery(const std::string& query); }; inline UP g_mathFinder; hyprwm-hyprlauncher-c682906/src/finders/unicode/000077500000000000000000000000001517324736300217075ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/finders/unicode/UnicodeFinder.cpp000066400000000000000000000066041517324736300251370ustar00rootroot00000000000000#include "UnicodeFinder.hpp" #include "../../helpers/Log.hpp" #include "../Fuzzy.hpp" #include "../Cache.hpp" #include #include #include #include #include using namespace Hyprutils::OS; class CUnicodeEntry : public IFinderResult { public: CUnicodeEntry() = default; virtual ~CUnicodeEntry() = default; virtual const std::string& fuzzable() { return m_fuzzable; } virtual eFinderTypes type() { return FINDER_UNICODE; } virtual uint32_t frequency() { return m_frequency; } virtual const std::string& name() { return m_unicode; } virtual void run() { Debug::log(TRACE, "Copying {} with wl-copy", m_unicode); g_unicodeFinder->m_entryFrequencyCache->incrementCachedEntry(m_unicode); m_frequency = g_unicodeFinder->m_entryFrequencyCache->getCachedEntry(m_unicode); CProcess proc("wl-copy", {m_unicode}); proc.runAsync(); } std::string m_name, m_unicode, m_fuzzable; uint32_t m_frequency = 0; }; static bool isSurrogate(UChar32 cp) { return cp >= 0xD800 && cp <= 0xDFFF; } CUnicodeFinder::CUnicodeFinder() : m_entryFrequencyCache(makeUnique("unicode")) { ; } void CUnicodeFinder::init() { for (UChar32 cp = 0; cp <= 0x10FFFF; ++cp) { if (isSurrogate(cp)) continue; auto category = u_charType(cp); if (category == U_UNASSIGNED || category == U_PRIVATE_USE_CHAR || category == U_SURROGATE) continue; UErrorCode status = U_ZERO_ERROR; char nameBuf[256]; int32_t len = u_charName(cp, U_EXTENDED_CHAR_NAME, nameBuf, sizeof(nameBuf), &status); std::vector dynBuf; if (status == U_BUFFER_OVERFLOW_ERROR && len > 0) { dynBuf.resize(len + 1); status = U_ZERO_ERROR; len = u_charName(cp, U_EXTENDED_CHAR_NAME, dynBuf.data(), (int32_t)dynBuf.size(), &status); } if (U_FAILURE(status) || len <= 0) continue; const char* name = (dynBuf.empty() ? nameBuf : dynBuf.data()); icu::UnicodeString us; us.append(cp); std::string utf8; us.toUTF8String(utf8); auto& e = m_unicodeEntryCache.emplace_back(makeShared()); e->m_unicode = utf8; e->m_name = name; e->m_fuzzable = name; std::ranges::transform(e->m_fuzzable, e->m_fuzzable.begin(), ::tolower); std::ranges::transform(e->m_name, e->m_name.begin(), ::toupper); e->m_frequency = g_unicodeFinder->m_entryFrequencyCache->getCachedEntry(e->m_unicode); m_unicodeEntryCacheGeneric.emplace_back(e); } } std::vector CUnicodeFinder::getResultsForQuery(const std::string& query) { std::vector results; auto fuzzed = Fuzzy::getNResults(m_unicodeEntryCacheGeneric, query, MAX_RESULTS_PER_FINDER); results.reserve(fuzzed.size()); for (const auto& f : fuzzed) { if (!f) continue; const auto p = reinterpretPointerCast(f); results.emplace_back(SFinderResult{ .label = p->m_unicode + " -> " + p->m_name, .icon = "", .result = p, }); } return results; } hyprwm-hyprlauncher-c682906/src/finders/unicode/UnicodeFinder.hpp000066400000000000000000000012371517324736300251410ustar00rootroot00000000000000#pragma once #include "../IFinder.hpp" class CUnicodeEntry; class CEntryCache; class CUnicodeFinder : public IFinder { public: CUnicodeFinder(); virtual ~CUnicodeFinder() = default; virtual std::vector getResultsForQuery(const std::string& query); virtual void init(); private: std::vector> m_unicodeEntryCache; std::vector> m_unicodeEntryCacheGeneric; UP m_entryFrequencyCache; void cacheEntry(const std::string& path); friend class CUnicodeEntry; }; inline UP g_unicodeFinder; hyprwm-hyprlauncher-c682906/src/helpers/000077500000000000000000000000001517324736300202715ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/helpers/Log.hpp000066400000000000000000000041441517324736300215260ustar00rootroot00000000000000#pragma once #include #include #include enum eLogLevel : uint8_t { TRACE = 0, INFO, LOG, WARN, ERR, CRIT, NONE }; #define RASSERT(expr, reason, ...) \ if (!(expr)) { \ Debug::log(CRIT, "\n==========================================================================================\nASSERTION FAILED! \n\n{}\n\nat: line {} in {}", \ std::format(reason, ##__VA_ARGS__), __LINE__, \ ([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })().c_str()); \ std::abort(); \ } #define ASSERT(expr) RASSERT(expr, "?") namespace Debug { constexpr const char* logLevelString(eLogLevel level) { switch (level) { case TRACE: return "TRACE"; break; case INFO: return "INFO"; break; case LOG: return "LOG"; break; case WARN: return "WARN"; break; case ERR: return "ERR"; break; case CRIT: return "CRITICAL"; break; default: return "??"; } } inline bool quiet = false; inline bool verbose = false; template void log(eLogLevel level, const std::string& fmt, Args&&... args) { if (!verbose && level == TRACE) return; if (quiet) return; if (level != NONE) std::println("[{}] {}", logLevelString(level), std::vformat(fmt, std::make_format_args(args...))); } };hyprwm-hyprlauncher-c682906/src/helpers/Memory.hpp000066400000000000000000000003721517324736300222540ustar00rootroot00000000000000#pragma once #include #include #include using namespace Hyprutils::Memory; #define SP CSharedPointer #define WP CWeakPointer #define UP CUniquePointer hyprwm-hyprlauncher-c682906/src/i18n/000077500000000000000000000000001517324736300174065ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/i18n/Engine.cpp000066400000000000000000000120631517324736300213210ustar00rootroot00000000000000#include "Engine.hpp" #include "../config/ConfigManager.hpp" static Hyprutils::I18n::CI18nEngine engine; // void I18n::initEngine() { engine.setFallbackLocale("en_US"); // ar (Arabic) engine.registerEntry("ar", TXT_KEY_SEARCH_SOMETHING, "بحث..."); // as_IN (Assamese) engine.registerEntry("as_IN", TXT_KEY_SEARCH_SOMETHING, "অনুসন্ধান..."); // ca_ES (Catalan) engine.registerEntry("ca_ES", TXT_KEY_SEARCH_SOMETHING, "Cercar..."); // cs_CZ (Czech) engine.registerEntry("cs_CZ", TXT_KEY_SEARCH_SOMETHING, "Hledat..."); // da_DK (Danish) engine.registerEntry("da_DK", TXT_KEY_SEARCH_SOMETHING, "Søg..."); // de_DE (German) engine.registerEntry("de_DE", TXT_KEY_SEARCH_SOMETHING, "Suche..."); // el_GR (Greek) engine.registerEntry("el_GR", TXT_KEY_SEARCH_SOMETHING, "Αναζήτηστε..."); // en_US (English) engine.registerEntry("en_US", TXT_KEY_SEARCH_SOMETHING, "Search..."); // es (Spanish) engine.registerEntry("es", TXT_KEY_SEARCH_SOMETHING, "Buscar..."); // et_EE (Estonian) engine.registerEntry("et_EE", TXT_KEY_SEARCH_SOMETHING, "Otsi..."); // fa (Farsi/Persian) engine.registerEntry("fa", TXT_KEY_SEARCH_SOMETHING, "جستجو..."); // fi_FI (Finnish) engine.registerEntry("fi_FI", TXT_KEY_SEARCH_SOMETHING, "Hae..."); // fr_FR (French) engine.registerEntry("fr_FR", TXT_KEY_SEARCH_SOMETHING, "Rechercher..."); // he (Hebrew) engine.registerEntry("he", TXT_KEY_SEARCH_SOMETHING, "חיפוש..."); // hi_IN (Hindi) engine.registerEntry("hi_IN", TXT_KEY_SEARCH_SOMETHING, "खोजें..."); // hu_HU (Hungarian) engine.registerEntry("hu_HU", TXT_KEY_SEARCH_SOMETHING, "Keresés..."); // id_ID (Indonesian) engine.registerEntry("id_ID", TXT_KEY_SEARCH_SOMETHING, "Cari..."); // it_IT (Italian) engine.registerEntry("it_IT", TXT_KEY_SEARCH_SOMETHING, "Cerca..."); // ja_JP (Japanese) engine.registerEntry("ja_JP", TXT_KEY_SEARCH_SOMETHING, "検索..."); // ko_KR (Korean) engine.registerEntry("ko_KR", TXT_KEY_SEARCH_SOMETHING, "검색..."); // ku (Kurdish) engine.registerEntry("ku", TXT_KEY_SEARCH_SOMETHING, "Bigere..."); // lt_LT (Lithuanian) engine.registerEntry("lt_LT", TXT_KEY_SEARCH_SOMETHING, "Paieška..."); // lv_LV (Latvian) engine.registerEntry("lv_LV", TXT_KEY_SEARCH_SOMETHING, "Meklēt..."); // ml_IN (Malayalam) engine.registerEntry("ml_IN", TXT_KEY_SEARCH_SOMETHING, "തിരയുക..."); // nb_NO (Norwegian Bokmål) engine.registerEntry("nb_NO", TXT_KEY_SEARCH_SOMETHING, "Søk..."); // nl_NL (Dutch Netherlands) engine.registerEntry("nl_NL", TXT_KEY_SEARCH_SOMETHING, "Zoeken..."); // pl_PL (Polish) engine.registerEntry("pl_PL", TXT_KEY_SEARCH_SOMETHING, "Szukaj..."); // pt_BR (Portuguese BR) engine.registerEntry("pt_BR", TXT_KEY_SEARCH_SOMETHING, "Buscar..."); // pt_PT (Portuguese Portugal) engine.registerEntry("pt_PT", TXT_KEY_SEARCH_SOMETHING, "Procurar..."); // pa_IN (Punjabi) engine.registerEntry("pa_IN", TXT_KEY_SEARCH_SOMETHING, "ਖੋਜ..."); // ro_RO (Romanian) engine.registerEntry("ro_RO", TXT_KEY_SEARCH_SOMETHING, "Caută..."); // ru_RU (Russian) engine.registerEntry("ru_RU", TXT_KEY_SEARCH_SOMETHING, "Поиск..."); // sl_SI (Slovenian) engine.registerEntry("sl_SI", TXT_KEY_SEARCH_SOMETHING, "Iščite..."); // sr_RS (Serbian) engine.registerEntry("sr_RS", TXT_KEY_SEARCH_SOMETHING, "Тражи..."); // sr_RS@latin (Serbian Latin) engine.registerEntry("sr_RS@latin", TXT_KEY_SEARCH_SOMETHING, "Traži..."); // sv_SE (Swedish) engine.registerEntry("sv_SE", TXT_KEY_SEARCH_SOMETHING, "Sök..."); // ta_IN (Tamil) engine.registerEntry("ta_IN", TXT_KEY_SEARCH_SOMETHING, "தேடவும்..."); // te_IN (Telugu) engine.registerEntry("te_IN", TXT_KEY_SEARCH_SOMETHING, "శోధన..."); // tr_TR (Turkish) engine.registerEntry("tr_TR", TXT_KEY_SEARCH_SOMETHING, "Ara..."); // tt_RU (Tatar) engine.registerEntry("tt_RU", TXT_KEY_SEARCH_SOMETHING, "Эзләү..."); // uk_UA (Ukrainian) engine.registerEntry("uk_UA", TXT_KEY_SEARCH_SOMETHING, "Пошук..."); // vi_VN (Vietnamese) engine.registerEntry("vi_VN", TXT_KEY_SEARCH_SOMETHING, "Tìm kiếm..."); // zh_CN (Simplified Chinese) engine.registerEntry("zh_CN", TXT_KEY_SEARCH_SOMETHING, "搜索..."); // zh_TW (Traditional Chinese) engine.registerEntry("zh_TW", TXT_KEY_SEARCH_SOMETHING, "搜尋..."); // ne_NP (Nepali) engine.registerEntry("ne_NP", TXT_KEY_SEARCH_SOMETHING, "खोज..."); } std::string I18n::localize(eTextKeys key, const Hyprutils::I18n::translationVarMap& vars) { static auto POVERRIDELOCALE = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "locale:override"); const auto LOCALE = std::string_view{*POVERRIDELOCALE}.empty() ? engine.getSystemLocale().locale() : std::string{*POVERRIDELOCALE}; return engine.localizeEntry(LOCALE, key, vars); } hyprwm-hyprlauncher-c682906/src/i18n/Engine.hpp000066400000000000000000000005131517324736300213230ustar00rootroot00000000000000#pragma once #include #include #include namespace I18n { //NOLINTNEXTLINE enum eTextKeys : uint64_t { TXT_KEY_SEARCH_SOMETHING = 0, }; void initEngine(); std::string localize(eTextKeys key, const Hyprutils::I18n::translationVarMap& vars); }; hyprwm-hyprlauncher-c682906/src/main.cpp000066400000000000000000000105611517324736300202620ustar00rootroot00000000000000#include "ui/UI.hpp" #include "helpers/Log.hpp" #include "finders/desktop/DesktopFinder.hpp" #include "finders/unicode/UnicodeFinder.hpp" #include "finders/math/MathFinder.hpp" #include "finders/ipc/IPCFinder.hpp" #include "finders/font/FontFinder.hpp" #include "socket/ClientSocket.hpp" #include "socket/ServerSocket.hpp" #include "query/QueryProcessor.hpp" #include "config/ConfigManager.hpp" #include "i18n/Engine.hpp" #include #include using namespace Hyprutils::String; static void printHelp() { std::cout << "Hyprlauncher usage: hyprlauncher [arg [...]].\n\nArguments:\n" << " -d | --daemon | Do not open after initializing\n" << " -o | --options \"a,b,c\" | Pass an explicit option array\n" << " -m | --dmenu | Pass an option list in dmenu-style (stdin, newline-separated)\n" << " -t | --toggle | When running with this option, toggle instead of opening\n" << " -h | --help | Print this menu\n" << " -v | --version | Print version info\n" << " | --quiet | Disable all logging\n" << " | --verbose | Enable too much logging\n" << std::endl; } static void printVersion() { std::cout << "Hyprlauncher v" << HYPRLAUNCHER_VERSION << std::endl; } static std::vector parseExplicitFromStdin() { Debug::log(TRACE, "Parsing stdin for dmenu mode"); std::vector result; std::string line; while (std::getline(std::cin, line)) { result.emplace_back(std::move(line)); } Debug::log(TRACE, "Read {} options from stdin", result.size()); return result; } int main(int argc, char** argv, char** envp) { bool openByDefault = true, dmenuMode = false, toggle = false; std::vector explicitOptions; for (int i = 1; i < argc; ++i) { std::string_view sv{argv[i]}; if (sv == "--verbose") { Debug::verbose = true; continue; } else if (sv == "--quiet") { Debug::quiet = true; continue; } else if (sv == "-d" || sv == "--daemon") { openByDefault = false; continue; } else if (sv == "-h" || sv == "--help") { printHelp(); return 0; } else if (sv == "-v" || sv == "--version") { printVersion(); return 0; } else if (sv == "-m" || sv == "--dmenu") { dmenuMode = true; continue; } else if (sv == "-o" || sv == "--options") { if (i + 1 >= argc) { Debug::log(ERR, "Missing argument for --options", sv); return 1; } CConstVarList vars(argv[i + 1], 0, ',', false); for (const auto& e : vars) { explicitOptions.emplace_back(e); } ++i; } else if (sv == "-t" || sv == "--toggle") { toggle = true; } else { Debug::log(ERR, "Unrecognized argument: {}", sv); return 1; } } auto socket = makeShared(); if (dmenuMode) explicitOptions = parseExplicitFromStdin(); if (socket->m_connected) { Debug::log(TRACE, "Active instance already, opening launcher."); if (!explicitOptions.empty()) socket->sendOpenWithOptions(explicitOptions); else toggle ? socket->sendToggle() : socket->sendOpen(); return 0; } g_serverIPCSocket = makeUnique(); g_desktopFinder = makeUnique(); g_unicodeFinder = makeUnique(); g_mathFinder = makeUnique(); g_ipcFinder = makeUnique(); g_fontFinder = makeUnique(); g_desktopFinder->init(); g_unicodeFinder->init(); g_mathFinder->init(); g_ipcFinder->init(); g_fontFinder->init(); socket.reset(); I18n::initEngine(); if (!explicitOptions.empty()) { g_ipcFinder->setData(explicitOptions); g_queryProcessor->overrideQueryProvider(g_ipcFinder); } g_configManager = makeUnique(); g_configManager->parse(); g_ui = makeUnique(openByDefault); g_ui->run(); return 0; } hyprwm-hyprlauncher-c682906/src/query/000077500000000000000000000000001517324736300177745ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/query/QueryProcessor.cpp000066400000000000000000000071211517324736300235060ustar00rootroot00000000000000#include "QueryProcessor.hpp" #include "../ui/UI.hpp" #include "../config/ConfigManager.hpp" #include "../finders/desktop/DesktopFinder.hpp" #include "../finders/unicode/UnicodeFinder.hpp" #include "../finders/math/MathFinder.hpp" #include "../finders/font/FontFinder.hpp" #include using namespace Hyprutils::Utils; static WP finderForName(const std::string& x) { if (x == "desktop") return g_desktopFinder; if (x == "unicode") return g_unicodeFinder; if (x == "math") return g_mathFinder; return WP{}; } static std::pair, bool> finderForPrefix(const char x) { static auto PDEFAULTFINDER = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "finders:default_finder"); static auto PDESKTOPPREFIX = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "finders:desktop_prefix"); static auto PUNICODEPREFIX = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "finders:unicode_prefix"); static auto PMATHPREFIX = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "finders:math_prefix"); static auto PFONTPREFIX = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "finders:font_prefix"); if (x == (*PDESKTOPPREFIX)[0]) return {g_desktopFinder, true}; if (x == (*PUNICODEPREFIX)[0]) return {g_unicodeFinder, true}; if (x == (*PMATHPREFIX)[0]) return {g_mathFinder, true}; if (x == (*PFONTPREFIX)[0]) return {g_fontFinder, true}; return {finderForName(*PDEFAULTFINDER), false}; } CQueryProcessor::CQueryProcessor() { m_queryThread = std::thread([this] { while (!m_quit) { std::unique_lock lk(m_threadMutex); m_threadCV.wait(lk, [this] { return m_event; }); m_event = false; if (m_quit) break; while (!m_quit && m_newQuery) { process(); } } }); } CQueryProcessor::~CQueryProcessor() { m_quit = true; m_pendingQuery = "exit"; m_event = true; m_threadCV.notify_all(); m_queryThread.join(); } void CQueryProcessor::scheduleQueryUpdate(const std::string& str) { m_queryStrMutex.lock(); m_pendingQuery = str; m_newQuery = true; m_event = true; m_queryStrMutex.unlock(); m_threadCV.notify_all(); } void CQueryProcessor::overrideQueryProvider(WP finder) { std::lock_guard lg(m_processingMutex); m_overrideFinder = finder; } // Only ran on process thread void CQueryProcessor::process() { CScopeGuard x([this] { m_newQuery = false; }); if (m_quit) return; m_queryStrMutex.lock(); std::string query = m_pendingQuery; m_pendingQuery = ""; m_queryStrMutex.unlock(); WP FINDER; bool eat = false; if (!m_overrideFinder) { const auto [F, e] = finderForPrefix(query[0]); if (e && query.size() == 1) return; FINDER = F; eat = e; } else FINDER = m_overrideFinder; if (query.empty() && !m_overrideFinder) { if (g_ui) g_ui->m_backend->addIdle([] mutable { g_ui->updateResults({}); }); return; } auto RESULTS = FINDER ? FINDER->getResultsForQuery(eat ? query.substr(1) : query) : std::vector{}; if (g_ui && g_ui->m_backend) g_ui->m_backend->addIdle([r = std::move(RESULTS)] mutable { g_ui->updateResults(std::move(r)); }); } hyprwm-hyprlauncher-c682906/src/query/QueryProcessor.hpp000066400000000000000000000014531517324736300235150ustar00rootroot00000000000000#pragma once #include #include #include #include "../helpers/Memory.hpp" class IFinder; class CQueryProcessor { public: CQueryProcessor(); ~CQueryProcessor(); void scheduleQueryUpdate(const std::string& str); void overrideQueryProvider(WP finder); private: void process(); std::condition_variable m_threadCV; std::mutex m_threadMutex, m_queryStrMutex, m_processingMutex; std::thread m_queryThread; std::string m_pendingQuery; bool m_quit = false, m_event = false; bool m_newQuery = false; WP m_overrideFinder; }; inline UP g_queryProcessor = makeUnique();hyprwm-hyprlauncher-c682906/src/socket/000077500000000000000000000000001517324736300201175ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/socket/ClientSocket.cpp000066400000000000000000000037361517324736300232230ustar00rootroot00000000000000#include "ClientSocket.hpp" #include #include constexpr const char* SOCKET_NAME = ".hyprlauncher.sock"; static SP g_coreImpl; CClientIPCSocket::CClientIPCSocket() { const auto RTDIR = getenv("XDG_RUNTIME_DIR"); if (!RTDIR) return; m_socketPath = RTDIR + std::string{"/"} + SOCKET_NAME; m_socket = Hyprwire::IClientSocket::open(m_socketPath); if (!m_socket) return; g_coreImpl = makeShared(1); m_socket->addImplementation(g_coreImpl); if (!m_socket->waitForHandshake()) return; auto spec = m_socket->getSpec(g_coreImpl->protocol()->specName()); if (!spec) { m_socket.reset(); return; } m_manager = makeShared(m_socket->bindProtocol(g_coreImpl->protocol(), 1)); if (!m_manager) { m_socket.reset(); return; } m_connected = true; } void CClientIPCSocket::sendOpen() { m_manager->sendSetOpenState(1 /* open */); } void CClientIPCSocket::sendClose() { m_manager->sendSetOpenState(2 /* close */); } void CClientIPCSocket::sendToggle() { m_manager->sendSetOpenState(0 /* toggle */); } void CClientIPCSocket::sendOpenWithOptions(const std::vector& opts) { std::vector optsC; optsC.reserve(opts.size()); for (const auto& o : opts) { optsC.emplace_back(o.c_str()); } m_info = makeShared(m_manager->sendGetInfoObject()); m_info->setOpenState([this](uint32_t open) { if (!open && !m_canExit) { m_canExit = true; std::println("Exited without selection"); } }); m_info->setSelectionMade([this](const char* sel) { std::println("{}", sel); m_canExit = true; }); m_manager->sendOpenWithOptions(optsC); while (!m_canExit && m_socket->dispatchEvents(true)) { // wait for selection ; } } hyprwm-hyprlauncher-c682906/src/socket/ClientSocket.hpp000066400000000000000000000012331517324736300232160ustar00rootroot00000000000000#pragma once #include #include #include "../helpers/Memory.hpp" class CClientIPCSocket { public: CClientIPCSocket(); ~CClientIPCSocket() = default; bool m_connected = false; void sendOpen(); void sendClose(); void sendToggle(); void sendOpenWithOptions(const std::vector& opts); private: SP m_socket; SP m_manager; SP m_info; bool m_canExit = false; std::string m_socketPath = ""; }; hyprwm-hyprlauncher-c682906/src/socket/ServerSocket.cpp000066400000000000000000000052441517324736300232470ustar00rootroot00000000000000#include "ServerSocket.hpp" #include "../ui/UI.hpp" #include "../query/QueryProcessor.hpp" #include "../finders/ipc/IPCFinder.hpp" #include #include constexpr const char* SOCKET_NAME = ".hyprlauncher.sock"; static SP g_coreImpl; CServerIPCSocket::CServerIPCSocket() { const auto RTDIR = getenv("XDG_RUNTIME_DIR"); if (!RTDIR) return; m_socketPath = RTDIR + std::string{"/"} + SOCKET_NAME; std::error_code ec; std::filesystem::remove(m_socketPath, ec); m_socket = Hyprwire::IServerSocket::open(m_socketPath); if (!m_socket) return; g_coreImpl = makeShared(1, [this](SP obj) { auto manager = m_managers.emplace_back(makeShared(std::move(obj))); manager->setSetOpenState([this](uint32_t state) { setOpenState(state); }); manager->setOpenWithOptions([this](std::vector state) { openWithOptions(state); }); manager->setGetInfoObject([this, m = WP{manager}](uint32_t seq) { if (!m) return; // protocol error auto x = m_infos.emplace_back(makeShared(m_socket->createObject(m->getObject()->client(), m->getObject(), "hyprlauncher_core_info", seq))); x->setDestroy([this, weak = WP{x}] { std::erase(m_infos, weak); }); x->setOnDestroy([this, weak = WP{x}] { std::erase(m_infos, weak); }); }); manager->setDestroy([this, weak = WP{manager}] { std::erase(m_managers, weak); }); manager->setOnDestroy([this, weak = WP{manager}] { std::erase(m_managers, weak); }); }); m_socket->addImplementation(g_coreImpl); } void CServerIPCSocket::setOpenState(uint32_t state) { switch (state) { case 0: g_ui->setWindowOpen(!g_ui->windowOpen()); break; case 1: g_ui->setWindowOpen(true); break; case 2: g_ui->setWindowOpen(false); break; default: break; } } void CServerIPCSocket::openWithOptions(const std::vector& options) { if (g_ui->windowOpen()) return; g_ipcFinder->setData(options); g_queryProcessor->overrideQueryProvider(g_ipcFinder); g_ui->setWindowOpen(true); } void CServerIPCSocket::sendOpenState(bool open) { for (const auto& i : m_infos) { i->sendOpenState(open); } } void CServerIPCSocket::sendSelectionMade(const std::string& s) { for (const auto& i : m_infos) { i->sendSelectionMade(s.c_str()); } } hyprwm-hyprlauncher-c682906/src/socket/ServerSocket.hpp000066400000000000000000000015111517324736300232450ustar00rootroot00000000000000#pragma once #include #include #include "../helpers/Memory.hpp" class CServerIPCSocket { public: CServerIPCSocket(); ~CServerIPCSocket() = default; void sendOpenState(bool open); void sendSelectionMade(const std::string& s); private: void setOpenState(uint32_t state); void openWithOptions(const std::vector& options); SP m_socket; std::string m_socketPath = ""; std::vector> m_managers; std::vector> m_infos; friend class CUI; }; inline UP g_serverIPCSocket; hyprwm-hyprlauncher-c682906/src/ui/000077500000000000000000000000001517324736300172445ustar00rootroot00000000000000hyprwm-hyprlauncher-c682906/src/ui/ResultButton.cpp000066400000000000000000000107041517324736300224240ustar00rootroot00000000000000#include "ResultButton.hpp" #include "../finders/IFinder.hpp" #include "UI.hpp" CResultButton::CResultButton() { const auto FONT_SIZE = Hyprtoolkit::CFontSize{Hyprtoolkit::CFontSize::HT_FONT_TEXT}.ptSize(); m_lastFontSize = FONT_SIZE; const auto BG_HEIGHT = (FONT_SIZE * 2.F) + 4.F; m_background = Hyprtoolkit::CRectangleBuilder::begin() ->color([]() { auto c = g_ui->m_backend->getPalette()->m_colors.accent.darken(0.3F); c.a = 0.F; return c; }) ->rounding(g_ui->m_backend->getPalette()->m_vars.smallRounding) ->size({Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, Hyprtoolkit::CDynamicSize::HT_SIZE_ABSOLUTE, {1.F, BG_HEIGHT}}) ->commence(); m_container = Hyprtoolkit::CRowLayoutBuilder::begin()->size({Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, {1, 1}})->gap(4)->commence(); m_container->setMargin(4); m_icon = Hyprtoolkit::CImageBuilder::begin() ->size({Hyprtoolkit::CDynamicSize::HT_SIZE_ABSOLUTE, Hyprtoolkit::CDynamicSize::HT_SIZE_ABSOLUTE, {0.7F * BG_HEIGHT, 0.7F * BG_HEIGHT}}) ->commence(); m_iconPlaceholder = Hyprtoolkit::CNullBuilder::begin() ->size({Hyprtoolkit::CDynamicSize::HT_SIZE_ABSOLUTE, Hyprtoolkit::CDynamicSize::HT_SIZE_ABSOLUTE, {0.7F * BG_HEIGHT, 0.7F * BG_HEIGHT}}) ->commence(); m_icon->setPositionMode(Hyprtoolkit::IElement::HT_POSITION_ABSOLUTE); m_icon->setPositionFlag(Hyprtoolkit::IElement::HT_POSITION_FLAG_VCENTER, true); m_iconPlaceholder->setPositionMode(Hyprtoolkit::IElement::HT_POSITION_ABSOLUTE); m_iconPlaceholder->setPositionFlag(Hyprtoolkit::IElement::HT_POSITION_FLAG_VCENTER, true); m_label = Hyprtoolkit::CTextBuilder::begin() ->text(std::string{m_lastLabel}) ->align(Hyprtoolkit::HT_FONT_ALIGN_LEFT) ->size({Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, {1, 1}}) ->commence(); m_label->setPositionMode(Hyprtoolkit::IElement::HT_POSITION_ABSOLUTE); m_label->setPositionFlag(Hyprtoolkit::IElement::HT_POSITION_FLAG_LEFT, true); m_label->setPositionFlag(Hyprtoolkit::IElement::HT_POSITION_FLAG_VCENTER, true); m_background->addChild(m_container); m_container->addChild(m_label); } void CResultButton::setActive(bool active) { if (active == m_active) return; m_active = active; m_background->rebuild() ->color([this]() { auto c = g_ui->m_backend->getPalette()->m_colors.accent.darken(0.3F); c.a = m_active ? 0.4F : 0.F; return c; }) ->commence(); } void CResultButton::setLabel(const std::string& x, const std::string& icon, std::optional font, bool canHaveIcon) { if (const auto FONT_SIZE = Hyprtoolkit::CFontSize{Hyprtoolkit::CFontSize::HT_FONT_TEXT}.ptSize(); FONT_SIZE != m_lastFontSize) updatedFontSize(); if (icon != m_lastIcon) { m_lastIcon = canHaveIcon ? icon : "_____NONEXISTENT___ICON_____"; if (canHaveIcon) { auto iconDescription = g_ui->m_backend->systemIcons()->lookupIcon(icon); m_container->clearChildren(); if (!iconDescription || !iconDescription->exists()) { m_container->addChild(m_iconPlaceholder); m_container->addChild(m_label); } else { m_icon->rebuild()->icon(iconDescription)->commence(); m_container->addChild(m_icon); m_container->addChild(m_label); } } else { m_container->clearChildren(); m_container->addChild(m_label); } } if (x != m_lastLabel) { m_lastLabel = x; m_label->rebuild()->text(std::string{x})->fontFamily(font.value_or(g_ui->m_backend->getPalette()->m_vars.fontFamily))->commence(); } } void CResultButton::updatedFontSize() { if (!m_background) return; const auto FONT_SIZE = Hyprtoolkit::CFontSize{Hyprtoolkit::CFontSize::HT_FONT_TEXT}.ptSize(); m_background->rebuild()->size({Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, Hyprtoolkit::CDynamicSize::HT_SIZE_ABSOLUTE, {1.F, (FONT_SIZE * 2.F) + 4.F}})->commence(); m_lastFontSize = FONT_SIZE; } hyprwm-hyprlauncher-c682906/src/ui/ResultButton.hpp000066400000000000000000000024671517324736300224400ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #include #include "../helpers/Memory.hpp" struct SFinderResult; class CResultButton { public: CResultButton(); ~CResultButton() = default; SP m_background; SP m_icon; SP m_iconPlaceholder; SP m_container; SP m_label; bool m_added = false; void setActive(bool active); void setLabel(const std::string& x, const std::string& icon, std::optional font, bool canHaveIcon); private: void updatedFontSize(); bool m_active = false; int m_lastFontSize = 0.F; std::string m_lastLabel = "", m_lastIcon = ""; }; hyprwm-hyprlauncher-c682906/src/ui/UI.cpp000066400000000000000000000166621517324736300203000ustar00rootroot00000000000000#include "UI.hpp" #include "ResultButton.hpp" #include "../finders/desktop/DesktopFinder.hpp" #include "../query/QueryProcessor.hpp" #include "../socket/ServerSocket.hpp" #include "../helpers/Log.hpp" #include "../config/ConfigManager.hpp" #include "../i18n/Engine.hpp" #include #include using namespace Hyprutils::Math; using namespace Hyprutils::String; constexpr const size_t MAX_RESULTS_IN_LAUNCHER = 50; CUI::CUI(bool open) : m_openByDefault(open) { static auto PGRABFOCUS = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "general:grab_focus"); static auto PWINDOWSIZE = Hyprlang::CSimpleConfigValue(g_configManager->m_config.get(), "ui:window_size"); m_backend = Hyprtoolkit::IBackend::create(); m_background = Hyprtoolkit::CRectangleBuilder::begin() ->color([this] { return m_backend->getPalette()->m_colors.background; }) ->rounding(m_backend->getPalette()->m_vars.bigRounding) ->borderColor([this] { return m_backend->getPalette()->m_colors.accent.darken(0.2F); }) ->borderThickness(1) ->size({Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, {1, 1}}) ->commence(); m_layout = Hyprtoolkit::CColumnLayoutBuilder::begin()->size({Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, {1, 1}})->gap(4)->commence(); m_layout->setMargin(4); m_inputBox = Hyprtoolkit::CTextboxBuilder::begin() ->placeholder(I18n::localize(I18n::TXT_KEY_SEARCH_SOMETHING, {})) ->onTextEdited([](SP, const std::string& query) { g_queryProcessor->scheduleQueryUpdate(query); }) ->size({Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, Hyprtoolkit::CDynamicSize::HT_SIZE_ABSOLUTE, {1.F, 28.F}}) ->multiline(false) ->commence(); m_hr = Hyprtoolkit::CRectangleBuilder::begin() ->color([this] { return m_backend->getPalette()->m_colors.accent.darken(0.2F); }) ->size({Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, Hyprtoolkit::CDynamicSize::HT_SIZE_ABSOLUTE, {0.8F, 1.F}}) ->commence(); m_hr->setPositionMode(Hyprtoolkit::IElement::HT_POSITION_ABSOLUTE); m_hr->setPositionFlag(Hyprtoolkit::IElement::HT_POSITION_FLAG_HCENTER, true); m_scrollArea = Hyprtoolkit::CScrollAreaBuilder::begin() ->size({Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, Hyprtoolkit::CDynamicSize::HT_SIZE_ABSOLUTE, {1.F, 10.F}}) ->scrollY(true) ->commence(); m_scrollArea->setGrow(true); m_resultsLayout = Hyprtoolkit::CColumnLayoutBuilder::begin()->size({Hyprtoolkit::CDynamicSize::HT_SIZE_PERCENT, Hyprtoolkit::CDynamicSize::HT_SIZE_AUTO, {1, 1}})->gap(2)->commence(); m_background->addChild(m_layout); m_layout->addChild(m_inputBox); m_layout->addChild(m_hr); m_layout->addChild(m_scrollArea); m_scrollArea->addChild(m_resultsLayout); // m_window = Hyprtoolkit::CWindowBuilder::begin() ->appClass("hyprlauncher") ->type(Hyprtoolkit::HT_WINDOW_LAYER) ->preferredSize({(*PWINDOWSIZE).x, (*PWINDOWSIZE).y}) ->anchor(1 | 2 | 4 | 8) ->exclusiveZone(-1) ->layer(3) ->kbInteractive(*PGRABFOCUS ? 1 : 2) ->commence(); m_window->m_rootElement->addChild(m_background); m_window->m_events.keyboardKey.listenStatic([this](Hyprtoolkit::Input::SKeyboardKeyEvent e) { if (e.xkbKeysym == XKB_KEY_Escape) setWindowOpen(false); else if (e.xkbKeysym == XKB_KEY_Down) { if (m_activeElementId + 1 < m_currentResults.size()) m_activeElementId++; updateActive(); } else if (e.xkbKeysym == XKB_KEY_Up) { if (m_activeElementId > 0) m_activeElementId--; updateActive(); } else if (e.xkbKeysym == XKB_KEY_Return || e.xkbKeysym == XKB_KEY_KP_Enter) onSelected(); }); } CUI::~CUI() = default; void CUI::run() { m_resultButtons.reserve(MAX_RESULTS_IN_LAUNCHER); for (size_t i = 0; i < MAX_RESULTS_IN_LAUNCHER; ++i) { auto b = m_resultButtons.emplace_back(makeShared()); b->m_added = true; m_resultsLayout->addChild(b->m_background); } if (m_openByDefault) setWindowOpen(true); if (g_serverIPCSocket->m_socket) { m_backend->addFd(g_serverIPCSocket->m_socket->extractLoopFD(), [] { Debug::log(TRACE, "got an ipc event"); g_serverIPCSocket->m_socket->dispatchEvents(false); }); } m_backend->addFd(g_configManager->m_inotifyFd.get(), [] { g_configManager->onInotifyEvent(); }); m_backend->addFd(g_desktopFinder->m_inotifyFd.get(), [] { g_desktopFinder->onInotifyEvent(); }); m_backend->enterLoop(); } void CUI::setWindowOpen(bool open) { if (open == m_open) return; m_open = open; if (open) { m_inputBox->rebuild()->defaultText("")->commence(); updateResults({}); m_window->open(); m_inputBox->focus(); g_queryProcessor->scheduleQueryUpdate(""); } else { m_window->close(); g_queryProcessor->overrideQueryProvider(WP{}); } g_serverIPCSocket->sendOpenState(open); } void CUI::onSelected() { if (m_currentResults.size() <= m_activeElementId) return; g_serverIPCSocket->sendSelectionMade(m_currentResults.at(m_activeElementId).result->name()); m_currentResults.at(m_activeElementId).result->run(); setWindowOpen(false); g_queryProcessor->overrideQueryProvider(WP{}); } bool CUI::windowOpen() { return m_open; } void CUI::updateResults(std::vector&& results) { m_currentResults = std::move(results); m_activeElementId = 0; for (size_t i = 0; i < m_resultButtons.size(); ++i) { if (m_currentResults.size() <= i) m_resultButtons[i]->setLabel("", "", std::nullopt, false); else m_resultButtons[i]->setLabel(m_currentResults[i].label, m_currentResults[i].icon, m_currentResults[i].overrideFont, m_currentResults[i].hasIcon); } updateActive(); } void CUI::updateActive() { for (size_t i = 0; i < m_resultButtons.size(); ++i) { auto& b = m_resultButtons[i]; b->setActive(i == m_activeElementId); if (i >= m_currentResults.size() && b->m_added) m_resultsLayout->removeChild(b->m_background); else if (i < m_currentResults.size() && !b->m_added) m_resultsLayout->addChild(b->m_background); b->m_added = i < m_currentResults.size(); } // fit the scroll area const float CURRENT_SCROLL_Y = m_scrollArea->getCurrentScroll().y; const float BUTTON_HEIGHT = m_resultButtons[0]->m_background->size().y + 2 /* gap */; const float MIN_SCROLL_TO_SEE = (BUTTON_HEIGHT * (m_activeElementId + 1)) - (m_scrollArea->size().y); const float MAX_SCROLL_TO_SEE = (BUTTON_HEIGHT * m_activeElementId); if (MAX_SCROLL_TO_SEE <= MIN_SCROLL_TO_SEE) return; // wtf?? m_scrollArea->setScroll({0.F, std::clamp(CURRENT_SCROLL_Y, MIN_SCROLL_TO_SEE, MAX_SCROLL_TO_SEE)}); } hyprwm-hyprlauncher-c682906/src/ui/UI.hpp000066400000000000000000000033751517324736300203020ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #include #include "../helpers/Memory.hpp" #include "../finders/IFinder.hpp" class CResultButton; class CUI { public: CUI(bool open); ~CUI(); void run(); void setWindowOpen(bool open); bool windowOpen(); // WARNING: has to be called from within the main thread. NOT thread safe!! void updateResults(std::vector&& results); void updateActive(); private: void onSelected(); SP m_backend; SP m_window; SP m_background; SP m_layout; SP m_inputBox; SP m_hr; SP m_scrollArea; SP m_resultsLayout; std::vector m_currentResults; std::vector> m_resultButtons; bool m_open = false; bool m_openByDefault = true; size_t m_activeElementId = 0; friend class CQueryProcessor; friend class CResultButton; }; inline UP g_ui;