👷 add new page

This commit is contained in:
Vladislav Nepogodin 2022-07-03 03:47:20 +04:00
parent c1799211c5
commit e328e3af7a
No known key found for this signature in database
GPG Key ID: B62C3D10C54D5DA9
11 changed files with 2604 additions and 299 deletions

91
Cargo.lock generated
View File

@ -12,10 +12,39 @@ dependencies = [
]
[[package]]
name = "anyhow"
version = "1.0.57"
name = "alpm"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
checksum = "bedac9e074b9368777268cff76c02d8b26d3e567642a52379aadd5c5e204e120"
dependencies = [
"alpm-sys",
"bitflags",
]
[[package]]
name = "alpm-sys"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fee1afe3484685ecedf03aefd7904428befed7815687e48aa4b21b3417eeecf0"
dependencies = [
"pkg-config",
]
[[package]]
name = "alpm-utils"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09329cd59e48e140faceb66ec714d8b5fe28a414327b381ae9a8af47ab29a700"
dependencies = [
"alpm",
"pacmanconf",
]
[[package]]
name = "anyhow"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
[[package]]
name = "atk"
@ -61,8 +90,10 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "cachyos-hello"
version = "0.7.0"
version = "0.8.0"
dependencies = [
"alpm",
"alpm-utils",
"gdk",
"gdk-pixbuf",
"gettext-rs",
@ -70,6 +101,7 @@ dependencies = [
"glib",
"gtk",
"once_cell",
"pacmanconf",
"serde",
"serde_json",
"subprocess",
@ -77,9 +109,9 @@ dependencies = [
[[package]]
name = "cairo-rs"
version = "0.15.11"
version = "0.15.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62be3562254e90c1c6050a72aa638f6315593e98c5cdaba9017cedbabf0a5dee"
checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc"
dependencies = [
"bitflags",
"cairo-sys-rs",
@ -114,6 +146,12 @@ dependencies = [
"smallvec",
]
[[package]]
name = "cini"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "895f4cd6403d7bd85e13ff3ec635d58183e5e708e7bd70430a91d661b04548e2"
[[package]]
name = "field-offset"
version = "0.3.4"
@ -491,6 +529,15 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
[[package]]
name = "pacmanconf"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faba895e4237d5dfd3459a4c0ef807b3510e088d28e7f5fd71a72962af84fef4"
dependencies = [
"cini",
]
[[package]]
name = "pango"
version = "0.15.10"
@ -579,18 +626,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.39"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.18"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [
"proc-macro2",
]
@ -647,18 +694,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.137"
version = "1.0.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.137"
version = "1.0.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
dependencies = [
"proc-macro2",
"quote",
@ -667,9 +714,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.81"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
dependencies = [
"itoa",
"ryu",
@ -684,9 +731,9 @@ checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
[[package]]
name = "smallvec"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]]
name = "subprocess"
@ -700,9 +747,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.95"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [
"proc-macro2",
"quote",
@ -765,9 +812,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicode-ident"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]]
name = "version-compare"

View File

@ -1,11 +1,15 @@
[package]
name = "cachyos-hello"
version = "0.7.0"
version = "0.8.0"
authors = ["Vladislav Nepogodin <nepogodin.vlad@gmail.com>"]
license = "GPLv3"
edition = "2021"
[dependencies]
alpm = "2.2.1"
alpm-utils = "1.1.2"
pacmanconf = "1.0.0"
subprocess = "0.2.9"
once_cell = "1.12.0"
gettext-rs = { version = "0.7", features = ["gettext-system"] }

View File

@ -0,0 +1,702 @@
[
{
"name": "Browsers",
"icon": "browser",
"description": "Web browsing and communication",
"apps": [
{
"name": "Chromium",
"icon": "chromium",
"description": "Open Sourced Chrome Browser",
"pkg": "chromium",
"extra": []
},
{
"name": "Epiphany",
"icon": "epiphany",
"description": "Open source Web Browser",
"pkg": "epiphany",
"extra": []
},
{
"name": "Falkon",
"icon": "falkon",
"description": "Qt based Web Browser",
"pkg": "falkon",
"extra": []
},
{
"name": "Firefox",
"icon": "mozilla-firefox",
"description": "Mozilla Web Browser",
"pkg": "firefox",
"extra": []
},
{
"name": "Konqueror",
"icon": "konqueror",
"description": "Konqueror Web Browser",
"pkg": "konqueror",
"extra": []
},
{
"name": "Midori",
"icon": "midori",
"description": "Lightweight Webbrowser",
"pkg": "midori",
"extra": []
},
{
"name": "Netsurf",
"icon": "netsurf",
"description": "Light and Fast Web Browser",
"pkg": "netsurf",
"extra": []
},
{
"name": "Opera",
"icon": "opera",
"description": "Fast and secure webbrowser",
"pkg": "opera",
"extra": []
}
]
},
{
"name": "E-mail",
"icon": "mail-client",
"description": "E-mail, Calendar, Tasks",
"apps": [
{
"name": "Claws Mail",
"icon": "claws-mail",
"description": "Lightweight and fast GTK+ based Mail Client",
"pkg": "claws-mail",
"extra": []
},
{
"name": "Evolution",
"icon": "evolution",
"description": "Manage your email, contacts and schedule",
"pkg": "evolution",
"extra": []
},
{
"name": "Geary",
"icon": "geary",
"description": "Send and receive mail",
"pkg": "geary",
"extra": []
},
{
"name": "KMail",
"icon": "kmail",
"description": "KDE E-mail client",
"pkg": "kmail",
"extra": []
},
{
"name": "Sylpheed",
"icon": "sylpheed",
"description": "E-mail client",
"pkg": "sylpheed",
"extra": []
},
{
"name": "Thunderbird",
"icon": "thunderbird",
"description": "Send and receive mail, contacts and schedule",
"pkg": "thunderbird",
"extra": []
}
]
},
{
"name": "Office Suites",
"icon": "applications-office",
"description": "Office suites like MS Office",
"apps": [
{
"name": "Calligra Office",
"icon": "calligrawords",
"description": "Qt Based Office Suite",
"pkg": "calligra",
"extra": []
},
{
"name": "Free Office",
"icon": "freeoffice-planmaker",
"description": "Free Office suite compatible with Microsoft Office",
"pkg": "freeoffice",
"extra": []
},
{
"name": "Libre Office (Fresh)",
"icon": "libreoffice-main",
"description": "Open Source Office Application (Lastest)",
"pkg": "libreoffice-fresh",
"extra": []
},
{
"name": "Libre Office (Still)",
"icon": "libreoffice-main",
"description": "Open Source Office Application (Stable)",
"pkg": "libreoffice-still",
"extra": []
},
{
"name": "MS Office Online",
"icon": "ms-word",
"description": "Microsoft Office Online",
"pkg": "microsoft-office-web-jak",
"extra": []
}
]
},
{
"name": "Office Apps",
"icon": "gnumeric",
"description": "Stand alone applications",
"apps": [
{
"name": "Abiword",
"icon": "abiword",
"description": "Compose, Edit and view documents",
"pkg": "abiword",
"extra": []
},
{
"name": "Gnumeric",
"icon": "gnumeric",
"description": "A High Precision Spreadsheet Program",
"pkg": "gnumeric",
"extra": []
}
]
},
{
"name": "Printing",
"icon": "printer",
"description": "Support for printing CUPS, HPLIP",
"apps": [
{
"name": "Manjaro Printing",
"icon": "printer",
"description": "Printing Meta Package",
"pkg": "manjaro-printer",
"extra": []
}
]
},
{
"name": "PDF",
"icon": "pdfeditor",
"description": "PDF applications applications",
"apps": [
{
"name": "Epdfview",
"icon": "qpdfview",
"description": "Lightweight PDF document viewer",
"pkg": "epdfview",
"extra": []
},
{
"name": "Evince",
"icon": "evince",
"description": "View multi page documents",
"pkg": "evince",
"extra": []
},
{
"name": "Okular",
"icon": "okular",
"description": "Document Viewer",
"pkg": "Okular",
"extra": []
},
{
"name": "PDFMod",
"icon": "pdfmod",
"description": "Remove, extract and rotate pages in PDF Documents",
"pkg": "pdfmod",
"extra": []
},
{
"name": "Qpdfview",
"icon": "qpdfview",
"description": "Tabbed document viewer",
"pkg": "qpdfview",
"extra": []
}
]
},
{
"name": "E-Book",
"icon": "calibre",
"description": "E-book library apps",
"apps": [
{
"name": "Calibre",
"icon": "calibre-viewer",
"description": "The one stop solution to your e-book needs",
"pkg": "calibre",
"extra": []
},
{
"name": "FBReader",
"icon": "fbreader",
"description": "FBReader E-Book Reader",
"pkg": "fbreader",
"extra": []
}
]
},
{
"name": "Backup",
"icon": "deja-dup",
"description": "Backup utilites",
"apps": [
{
"name": "Deja Dup",
"icon": "deja-dup",
"description": "Keep your important documents safe from disater",
"pkg": "deja-dup",
"extra": []
},
{
"name": "Grsync",
"icon": "grsync",
"description": "Synchronize files and folders",
"pkg": "grsync",
"extra": []
},
{
"name": "Timeshift",
"icon": "timeshift",
"description": "A system restore utility for Linux",
"pkg": "timeshift",
"extra": []
}
]
},
{
"name": "Text Editors",
"icon": "text-editor",
"description": "Various editors for text or code",
"apps": [
{
"name": "Atom",
"icon": "atom",
"description": "A hackable text editor for the 21st Century",
"pkg": "atom",
"extra": []
},
{
"name": "Geany",
"icon": "geany",
"description": "A fast and lightweight IDE using GTK+",
"pkg": "geany",
"extra": []
},
{
"name": "Mousepad",
"icon": "mousepad",
"description": "Simple Text Editor",
"pkg": "mousepad",
"extra": []
},
{
"name": "Visual Studio Code OSS",
"icon": "visual-studio-code",
"description": "Microsoft Code Editor",
"pkg": "code",
"extra": []
},
{
"name": "Xed",
"icon": "xed",
"description": "A small and lightweight text editor. X Apps Project",
"pkg": "xed",
"extra": []
}
]
},
{
"name": "System Tools",
"icon": "disk-utility",
"description": "System utilities",
"apps": [
{
"name": "Etcher",
"icon": "etcher-electron",
"description": "Graphical tool to write ISO to USB",
"pkg": "etcher",
"extra": []
},
{
"name": "Gnome Disks",
"icon": "gnome-disks",
"description": "Disk management system for Gnome",
"pkg": "gnome-disk-utility",
"extra": []
},
{
"name": "Gparted",
"icon": "gparted",
"description": "Create, reorganize, and delete partitions",
"pkg": "gparted",
"extra": []
},
{
"name": "IsoUSB",
"icon": "usb-creator",
"description": "A graphical tool to copy a hybrid ISO onto a USB key.",
"pkg": "isousb",
"extra": []
},
{
"name": "Mintstick",
"icon": "mintstick",
"description": "Format or wirte imges to usb sticks (Linux Mint tool).",
"pkg": "mintstick",
"extra": []
},
{
"name": "Pamac",
"icon": "pamac-updater",
"description": "Update your System, Add/Remove Software from repo and AUR",
"pkg": "pamac",
"extra": []
},
{
"name": "Yay",
"icon": "terminal",
"description": "CLI AUR helper",
"pkg": "yay",
"extra": []
}
]
},
{
"name": "Security",
"icon": "security-high",
"description": "Security oriented utilities",
"apps": [
{
"name": "KeePassX",
"icon": "keepassx",
"description": "Cross Platform Password Manager",
"pkg": "keepassx",
"extra": []
},
{
"name": "SeaHorse",
"icon": "seahorse",
"description": "Manage your passwords and encryption keys",
"pkg": "seahorse",
"extra": []
},
{
"name": "VeraCrypt",
"icon": "veracrypt",
"description": "Disk encryption with strong security based on TrueCrypt",
"pkg": "veracrypt",
"extra": []
}
]
},
{
"name": "Virtual Computing",
"icon": "virt-manager",
"description": "Virtual Machine applications",
"apps": [
{
"name": "VirtualBox",
"icon": "virtualbox",
"description": "Run several virtual systems on a single host computer",
"pkg": "calibre",
"extra": []
},
{
"name": "Gnome Boxes",
"icon": "gnome-boxes",
"description": "Simple remote and virtual machines",
"pkg": "gnome-boxes",
"extra": []
}
]
},
{
"name": "Chat",
"icon": "internet-chat",
"description": "Online messaging and chat",
"apps": [
{
"name": "HexChat",
"icon": "hexchat",
"description": "Graphic IRC Client",
"pkg": "hexchat",
"extra": []
},
{
"name": "Pidgin Messenger",
"icon": "pidgin",
"description": "Instant messaging Client",
"pkg": "pidgin",
"extra": []
}
]
},
{
"name": "File Sharing",
"icon": "transmission",
"description": "FTP and Torrent apps",
"apps": [
{
"name": "Filezilla",
"icon": "filezilla",
"description": "Graphical FTP/FTPS/SFTP browser",
"pkg": "filezilla",
"extra": []
},
{
"name": "qBittorrent",
"icon": "qbittorrent",
"description": "A Qt based Bittorrent Client",
"pkg": "qbittorrent",
"extra": []
},
{
"name": "Transmission GTK",
"icon": "transmission",
"description": "GTK based Bittorrent Client",
"pkg": "transmission-gtk",
"extra": []
},
{
"name": "QTransmission",
"icon": "transmission",
"description": "QT based Bittorrent Client",
"pkg": "transmission-qt",
"extra": []
}
]
},
{
"name": "Graphics Creating",
"icon": "applications-accessories",
"description": "Creating and editing graphics",
"apps": [
{
"name": "Blender",
"icon": "blender",
"description": "3D modeling and animation",
"pkg": "blender",
"extra": []
},
{
"name": "GIMP",
"icon": "gimp",
"description": "Create images and edit photographs",
"pkg": "gimp",
"extra": []
},
{
"name": "Inkscape",
"icon": "inkscape",
"description": "Vector Graphics Editor",
"pkg": "inkscape",
"extra": []
},
{
"name": "Krita",
"icon": "krita",
"description": "Digital Painting Creative Freedom",
"pkg": "krita",
"extra": []
},
{
"name": "Pinta",
"icon": "pinta",
"description": "Easy create and edit images",
"pkg": "pinta",
"extra": []
},
{
"name": "Tux Paint",
"icon": "tuxpaint",
"description": "Drawing program for children",
"pkg": "tuxpaint",
"extra": []
}
]
},
{
"name": "Graphics Organizing",
"icon": "applications-graphics",
"description": "Viewers and organizers",
"apps": [
{
"name": "Gpicview",
"icon": "gpicview",
"description": "Lightweight Image Viewer",
"pkg": "gpicview",
"extra": []
},
{
"name": "gThumb",
"icon": "gthumb",
"description": "View and organize your images",
"pkg": "gthumb",
"extra": []
},
{
"name": "Gwenview",
"icon": "gwenview",
"description": "Image Viewer",
"pkg": "gwenview",
"extra": []
},
{
"name": "Ristretto",
"icon": "ristretto",
"description": "Free and lightweight image viewer",
"pkg": "ristretto",
"extra": []
},
{
"name": "Shotwell",
"icon": "shotwell",
"description": "Popular Photo Manager",
"pkg": "shotwell",
"extra": []
},
{
"name": "Viewnior",
"icon": "viewnior",
"description": "GTK based Elegant Image Viewer",
"pkg": "viewnior",
"extra": []
}
]
},
{
"name": "Video/Movie",
"icon": "video-player",
"description": "Organize and play videos and movies",
"apps": [
{
"name": "Kodi",
"icon": "kodi",
"description": "Manage and view your media",
"pkg": "kodi",
"extra": []
},
{
"name": "Parole",
"icon": "parole",
"description": "Modern and simple media player",
"pkg": "parole",
"extra": []
},
{
"name": "SM Player",
"icon": "smplayer",
"description": "A great MPlayer front end",
"pkg": "smplayer",
"extra": []
},
{
"name": "Totem",
"icon": "totem",
"description": "Play movies",
"pkg": "totem",
"extra": []
},
{
"name": "VLC",
"icon": "vlc",
"description": "VLC media player, the openxource multimedia player",
"pkg": "vlc",
"extra": []
}
]
},
{
"name": "Audio",
"icon": "musicbrainz",
"description": "Audio players",
"apps": [
{
"name": "Audacious",
"icon": "audacious",
"description": "Listen to music",
"pkg": "audacious",
"extra": []
},
{
"name": "Clementine",
"icon": "clementine",
"description": "Play music files and internet radio",
"pkg": "clementine",
"extra": []
},
{
"name": "DeadBeeF",
"icon": "deadbeef",
"description": "Listen to music",
"pkg": "deadbeef",
"extra": []
},
{
"name": "Lollypop",
"icon": "lollypop",
"description": "Play and organize your music collection",
"pkg": "deadbeef",
"extra": []
},
{
"name": "Rhythmbox",
"icon": "rhythmbox",
"description": "Gnome music playing application",
"pkg": "rhythmbox",
"extra": []
}
]
},
{
"name": "Media recording/editing",
"icon": "kdenlive",
"description": "Audio and Video editing",
"apps": [
{
"name": "Audacity",
"icon": "audacity",
"description": "Record and Edit Audio files",
"pkg": "audacity",
"extra": []
},
{
"name": "Kdenlive",
"icon": "kdenlive",
"description": "Video Editor",
"pkg": "kdenlive",
"extra": []
},
{
"name": "OBS Studio",
"icon": "obs",
"description": "Open Source Streaming/Recording Application",
"pkg": "obs-studio",
"extra": []
},
{
"name": "Simple Screen Recorder",
"icon": "simple-ccsm",
"description": "Screen Capturing Application",
"pkg": "simplescreenrecorder",
"extra": []
}
]
}
]

View File

@ -0,0 +1,874 @@
[
{
"name": "Browsers",
"icon": "browser",
"description": "Web browsing and communication",
"apps": [
{
"name": "Vivaldi",
"icon": "vivaldi",
"description": "Configurable browser for power users",
"pkg": "vivaldi",
"extra": []
},
{
"name": "Chromium",
"icon": "chromium",
"description": "Fast and popular open source browser",
"pkg": "chromium",
"extra": []
},
{
"name": "Epiphany",
"icon": "epiphany",
"description": "Gnome Web Browser",
"pkg": "epiphany",
"filter": [
"advanced"
],
"extra": []
},
{
"name": "Firefox",
"icon": "mozilla-firefox",
"description": "Privacy oriented Web Browser",
"pkg": "firefox",
"extra": ["firefox-gnome-theme-maia"]
},
{
"name": "Midori",
"icon": "midori",
"description": "Lightweight Web browser",
"pkg": "midori",
"extra": []
},
{
"name": "Falkon",
"icon": "falkon",
"description": "KDE Web Browser",
"pkg": "falkon",
"filter": [
"advanced"
],
"desktop": [
"!gnome"
],
"extra": []
},
{
"name": "Konqueror",
"icon": "konqueror",
"description": "Konqueror Web Browser",
"pkg": "konqueror",
"filter": [
"advanced"
],
"desktop": [
"!gnome"
],
"extra": []
},
{
"name": "Opera",
"icon": "opera",
"description": "Fast and secure webbrowser",
"pkg": "opera",
"extra": []
}
]
},
{
"name": "E-mail",
"icon": "mail-client",
"description": "E-mail, Calendar, Tasks",
"apps": [
{
"name": "Claws Mail",
"icon": "claws-mail",
"description": "Lightweight and fast GTK+ based Mail Client",
"pkg": "claws-mail",
"filter": [
"advanced"
],
"extra": []
},
{
"name": "Evolution",
"icon": "evolution",
"description": "Manage your email, contacts and schedule",
"pkg": "evolution",
"extra": []
},
{
"name": "Geary",
"icon": "geary",
"description": "Send and receive mail",
"pkg": "geary",
"extra": []
},
{
"name": "KMail",
"icon": "kmail",
"description": "KDE E-mail client",
"pkg": "kmail",
"desktop": [
"!gnome"
],
"extra": []
},
{
"name": "Sylpheed",
"icon": "sylpheed",
"description": "E-mail client",
"pkg": "sylpheed",
"extra": []
},
{
"name": "Thunderbird",
"icon": "thunderbird",
"description": "Send and receive mail, contacts and schedule",
"pkg": "thunderbird",
"extra": []
}
]
},
{
"name": "Office Suites",
"icon": "applications-office",
"description": "Office suites like MS Office",
"apps": [
{
"name": "FreeOffice",
"icon": "ms-word",
"description": "Microsoft Office compatible suite",
"pkg": "freeoffice",
"extra": []
},
{
"name": "Libre Office (Fresh)",
"icon": "libreoffice-main",
"description": "Open Source Office Application (Latest)",
"pkg": "libreoffice-fresh",
"extra": []
},
{
"name": "Libre Office (Still)",
"icon": "libreoffice-main",
"description": "Open Source Office Application (Stable)",
"pkg": "libreoffice-still",
"extra": []
},
{
"name": "MS Office Online",
"icon": "ms-word",
"description": "Microsoft Office Online",
"pkg": "microsoft-office-web-jak",
"extra": []
},
{
"name": "OnlyOffice Desktop Editors",
"icon": "ms-word",
"description": "Light and free MS Office clone.",
"pkg": "onlyoffice-desktopeditors",
"extra": []
}
]
},
{
"name": "Extended language support",
"icon": "gnumeric",
"description": "Manjaro extended language support",
"apps": [
{
"name": "Manjaro Asian Input Support Fcitx",
"icon": "gnumeric",
"description": "Manjaro extended language support with fcitx support",
"pkg": "manjaro-asian-input-support-fcitx",
"extra": []
},
{
"name": "Manjaro Asian Input Support Ibus",
"icon": "gnumeric",
"description": "Manjaro extended language support with ibus support",
"pkg": "manjaro-asian-input-support-ibus",
"extra": []
}
]
},
{
"name": "E-Book",
"icon": "calibre",
"description": "E-book library apps",
"apps": [
{
"name": "Calibre",
"icon": "calibre-viewer",
"description": "The one stop solution to your e-book needs",
"pkg": "calibre",
"extra": []
},
{
"name": "FBReader",
"icon": "fbreader",
"description": "FBReader E-Book Reader",
"pkg": "fbreader",
"extra": []
}
]
},
{
"name": "Text Editors",
"icon": "text-editor",
"description": "Various editors for text or code",
"apps": [
{
"name": "Atom",
"icon": "atom",
"description": "A hackable text editor for the 21st Century",
"pkg": "atom",
"filter": [
"advanced"
],
"extra": []
},
{
"name": "Gedit",
"icon": "gedit",
"description": "Gnome text editor.",
"pkg": "gedit",
"extra": []
},
{
"name": "Micro",
"icon": "micro",
"description": "Inituitive Text Editor for terminal",
"pkg": "micro-manjaro",
"extra": []
},
{
"name": "Vim",
"icon": "gvim",
"description": "Keyboard controlled text-editor",
"pkg": "gvim",
"filter": [
"advanced"
],
"extra": []
},
{
"name": "Visual Studio Code OSS",
"icon": "visual-studio-code",
"description": "Microsoft Code Editor",
"pkg": "code",
"filter": [
"advanced"
],
"extra": []
},
{
"name": "Gnome builder",
"icon": "gedit",
"description": "An IDE for writing GNOME-based software",
"pkg": "gnome-builder",
"filter": [
"advanced"
],
"extra": []
},
{
"name": "Xed",
"icon": "xed",
"description": "A small and lightweight text editor. X Apps Project",
"pkg": "xed",
"extra": []
}
]
},
{
"name": "Printing",
"icon": "printer",
"description": "Support for printing CUPS, HPLIP",
"apps": [
{
"name": "Manjaro Printing",
"icon": "printer",
"description": "Printing Meta Package",
"pkg": "manjaro-printer",
"extra": []
}
]
},
{
"name": "PDF",
"icon": "pdfeditor",
"description": "PDF applications applications",
"apps": [
{
"name": "Evince",
"icon": "evince",
"description": "View multi page documents",
"pkg": "evince",
"extra": []
},
{
"name": "Okular",
"icon": "okular",
"description": "Document Viewer",
"pkg": "okular",
"desktop": [
"!gnome"
],
"extra": []
},
{
"name": "PDFMod",
"icon": "pdfmod",
"description": "Remove, extract and rotate pages in PDF Documents",
"pkg": "pdfmod",
"extra": []
},
{
"name": "Xournalpp",
"icon": "xournalpp",
"description": "Easy pdf editing tool",
"pkg": "xournalpp",
"extra": []
},
{
"name": "Xreader",
"icon": "xreader",
"description": "Xreader is based on Atril and acts as document and PDF reader",
"pkg": "xreader",
"extra": []
},
{
"name": "Paperwork",
"icon": "paperwork",
"description": "Document manager for GNOME to manage scanned documents and PDFs",
"pkg": "paperwork",
"extra": []
}
]
},
{
"name": "Graphics Creating",
"icon": "applications-accessories",
"description": "Creating and editing graphics",
"apps": [
{
"name": "Blender",
"icon": "blender",
"description": "3D modeling and animation",
"pkg": "blender",
"extra": []
},
{
"name": "GIMP",
"icon": "gimp",
"description": "Create images and edit photographs",
"pkg": "gimp",
"extra": []
},
{
"name": "Inkscape",
"icon": "inkscape",
"description": "Vector Graphics Editor",
"pkg": "inkscape",
"extra": []
},
{
"name": "Krita",
"icon": "krita",
"description": "Digital Painting Creative Freedom",
"pkg": "krita",
"extra": []
},
{
"name": "Pinta",
"icon": "pinta",
"description": "Easy create and edit images",
"pkg": "pinta",
"extra": []
},
{
"name": "Tux Paint",
"icon": "tuxpaint",
"description": "Drawing program for children",
"pkg": "tuxpaint",
"extra": []
}
]
},
{
"name": "Photos",
"icon": "applications-graphics",
"description": "Viewers and organizers",
"apps": [
{
"name": "Gpicview",
"icon": "gpicview",
"description": "Lightweight Image Viewer",
"pkg": "gpicview",
"extra": []
},
{
"name": "gThumb",
"icon": "gthumb",
"description": "View and organize your images",
"pkg": "gthumb",
"extra": []
},
{
"name": "Gnome-photos",
"icon": "gthumb",
"description": "Access, organize, and share your photos on GNOME",
"pkg": "gnome-photos",
"extra": []
},
{
"name": "Ristretto",
"icon": "ristretto",
"description": "Free and lightweight image viewer",
"pkg": "ristretto",
"extra": []
},
{
"name": "Shotwell",
"icon": "shotwell",
"description": "Popular Photo Manager",
"pkg": "shotwell",
"extra": []
},
{
"name": "Viewnior",
"icon": "viewnior",
"description": "GTK based Elegant Image Viewer",
"pkg": "viewnior",
"extra": []
},
{
"name": "Eye of Gnome",
"icon": "eog",
"description": "An image viewing and cataloging program",
"pkg": "eog",
"extra": []
}
]
},
{
"name": "Video/Movie",
"icon": "video-player",
"description": "Organize and play videos and movies",
"apps": [
{
"name": "Kodi",
"icon": "kodi",
"description": "Manage and view your media",
"pkg": "kodi",
"extra": []
},
{
"name": "Parole",
"icon": "parole",
"description": "Modern and simple media player",
"pkg": "parole",
"extra": []
},
{
"name": "SM Player",
"icon": "smplayer",
"description": "A great MPlayer front end",
"pkg": "smplayer",
"extra": []
},
{
"name": "Totem",
"icon": "totem",
"description": "Play movies",
"pkg": "totem",
"extra": []
},
{
"name": "VLC",
"icon": "vlc",
"description": "VLC media player, the openxource multimedia player",
"pkg": "vlc",
"extra": []
},
{
"name": "Xplayer",
"icon": "xplayer",
"description": "Xplayer is based on Totem and plays both music an video",
"pkg": "xplayer",
"extra": []
}
]
},
{
"name": "Audio",
"icon": "musicbrainz",
"description": "Audio players",
"apps": [
{
"name": "Audacious",
"icon": "audacious",
"description": "Listen to music",
"pkg": "audacious",
"extra": []
},
{
"name": "Clementine",
"icon": "clementine",
"description": "Play music files and internet radio",
"pkg": "clementine",
"extra": []
},
{
"name": "DeadBeeF",
"icon": "deadbeef",
"description": "Listen to music",
"pkg": "deadbeef",
"extra": ["ffmpeg", "libmad", "mpg123"]
},
{
"name": "Lollypop",
"icon": "lollypop",
"description": "Play and organize your music collection",
"pkg": "lollypop",
"extra": []
},
{
"name": "Rhythmbox",
"icon": "rhythmbox",
"description": "Gnome music playing application",
"pkg": "rhythmbox",
"extra": []
}
]
},
{
"name": "Media recording/editing",
"icon": "kdenlive",
"description": "Audio and Video editing",
"apps": [
{
"name": "Audacity",
"icon": "audacity",
"description": "Record and Edit Audio files",
"pkg": "audacity",
"extra": []
},
{
"name": "Kdenlive",
"icon": "kdenlive",
"description": "Video Editor",
"pkg": "kdenlive",
"extra": []
},
{
"name": "OBS Studio",
"icon": "obs",
"description": "Open Source Streaming/Recording Application",
"pkg": "obs-studio",
"extra": []
},
{
"name": "Shotcut",
"icon": "shotcut",
"description": "Cross-platform Qt based Video Editor",
"pkg": "shotcut",
"extra": []
},
{
"name": "Openshot",
"icon": "openshot",
"description": "Award-winning free and open-source video editor",
"pkg": "obs-studio",
"extra": []
},
{
"name": "Pitivi",
"icon": "pitivi",
"description": "Editor for audio/video projects",
"pkg": "pitivi",
"extra": []
},
{
"name": "Simple Screen Recorder",
"icon": "simple-ccsm",
"description": "Screen Capturing Application",
"pkg": "simplescreenrecorder",
"extra": []
},
{
"name": "Flowblade",
"icon": "flowblade",
"description": "Multitrack non-linear video editor",
"pkg": "flowblade",
"extra": []
}
]
},
{
"name": "Chat",
"icon": "internet-chat",
"description": "Online messaging and chat",
"apps": [
{
"name": "HexChat",
"icon": "hexchat",
"description": "Graphic IRC Client",
"pkg": "hexchat",
"extra": []
},
{
"name": "Telegram",
"icon": "telegram-desktop",
"description": "Official Telegram Desktop client",
"pkg": "telegram-desktop",
"extra": []
},
{
"name": "Whatsapp web",
"icon": "whatsapp-web-jak",
"description": "Wrapper for whatsapp online",
"pkg": "whatsapp-web-jak",
"extra": []
},
{
"name": "Slack web",
"icon": "slack-web-jak",
"description": "Wrapper for slack online",
"pkg": "slack-web-jak",
"extra": []
},
{
"name": "Pidgin Messenger",
"icon": "pidgin",
"description": "Instant messaging Client",
"pkg": "pidgin",
"extra": []
}
]
},
{
"name": "File Sharing",
"icon": "transmission",
"description": "FTP and Torrent apps",
"filter": [
"advanced"
],
"apps": [
{
"name": "Deluge",
"icon": "deluge",
"description": "Python based Bittorrent Client",
"pkg": "deluge",
"extra": []
},
{
"name": "Filezilla",
"icon": "filezilla",
"description": "Graphical FTP/FTPS/SFTP browser",
"pkg": "filezilla",
"extra": []
},
{
"name": "Transmission GTK",
"icon": "transmission",
"description": "GTK based Bittorrent Client",
"pkg": "transmission-gtk",
"extra": []
},
{
"name": "QTransmission",
"icon": "transmission",
"description": "QT based Bittorrent Client",
"pkg": "transmission-qt",
"desktop": [
"!gnome"
],
"extra": []
},
{
"name": "Syncthing",
"icon": "syncthing-gtk",
"description": "Cross Platform file synchronization tool",
"pkg": "syncthing-gtk",
"extra": []
}
]
},
{
"name": "Backup",
"icon": "deja-dup",
"description": "Backup utilites",
"apps": [
{
"name": "Deja Dup",
"icon": "deja-dup",
"description": "Keep your important documents safe from disater",
"pkg": "deja-dup",
"extra": []
},
{
"name": "Grsync",
"icon": "grsync",
"description": "Synchronize files and folders",
"pkg": "grsync",
"filter": [
"advanced"
],
"extra": []
},
{
"name": "Timeshift",
"icon": "timeshift",
"description": "A system restore utility for Linux",
"pkg": "timeshift",
"extra": []
},
{
"name": "Backintime",
"icon": "deja-dup",
"description": "Simple backup system",
"pkg": "backintime",
"extra": []
},
{
"name": "Borg",
"icon": "deja-dup",
"description": "Deduplicating backup program with compression and authenticated encryption",
"pkg": "borg",
"filter": [
"advanced"
],
"extra": []
}
]
},
{
"name": "Password Manager",
"icon": "security-high",
"description": "Password Managers",
"apps": [
{
"name": "KeePassXC",
"icon": "keepassxc",
"description": "Cross Platform Password Manager",
"pkg": "keepassxc",
"extra": []
},
{
"name": "Bitwarden",
"icon": "bitwarden",
"description": "Secure and free cloud based password manager.",
"pkg": "bitwarden",
"extra": []
},
{
"name": "Enpass",
"icon": "enpass",
"description": "Premium Cross-platform Password Manager",
"pkg": "enpass",
"extra": []
}
]
},
{
"name": "System Tools",
"icon": "disk-utility",
"description": "System utilities",
"filter": [
"advanced"
],
"apps": [
{
"name": "Popsicle",
"icon": "mintstick",
"description": "Graphical tool to write ISO to USB",
"pkg": "popsicle",
"extra": []
},
{
"name": "Gnome Disks",
"icon": "gnome-disks",
"description": "Disk management system for Gnome",
"pkg": "gnome-disk-utility",
"desktop": [
"!kde"
],
"extra": []
},
{
"name": "Gparted",
"icon": "gparted",
"description": "Create, reorganize, and delete partitions",
"pkg": "gparted",
"extra": []
},
{
"name": "IsoUSB",
"icon": "mintstick",
"description": "A graphical tool to copy a hybrid ISO onto a USB key.",
"pkg": "isousb",
"extra": []
},
{
"name": "Mintstick",
"icon": "mintstick",
"description": "Format or write imges to usb sticks (Linux Mint tool).",
"pkg": "mintstick",
"extra": []
},
{
"name": "Ventoy",
"icon": "mintstick",
"description": "Tool for creating multiboot isos",
"pkg": "ventoy",
"extra": []
},
{
"name": "Alma",
"icon": "mintstick",
"description": "Tool for creating persistent live isos",
"pkg": "alma-git",
"extra": []
}
]
},
{
"name": "Virtual Computing",
"icon": "virt-manager",
"description": "Virtual Machine applications",
"filter": [
"advanced"
],
"apps": [
{
"name": "VirtualBox",
"icon": "virtualbox",
"description": "Run several virtual systems on a single host computer",
"pkg": "virtualbox",
"extra": [
"virtualbox-guest-iso"
]
},
{
"name": "Gnome Boxes",
"icon": "gnome-boxes",
"description": "Simple remote and virtual machines",
"pkg": "gnome-boxes",
"extra": []
},
{
"name": "Virt-manager",
"icon": "virt-manager",
"description": "Desktop user interface for managing virtual machines",
"pkg": "virt-manager",
"extra": []
}
]
}
]

View File

@ -1,5 +1,5 @@
project('cachyos-hello', 'rust',
version: '0.7.0',
version: '0.8.0',
license: 'GPLv3',
meson_version: '>=0.56.0',
default_options: ['buildtype=debugoptimized',

103
src/alpm_helper.rs Normal file
View File

@ -0,0 +1,103 @@
#[derive(Clone, Debug)]
#[repr(C)]
pub struct AlpmHelper {
pub pkg_list_install: Vec<String>,
pub pkg_list_removal: Vec<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum AlpmHelperResult {
NOTHING,
REMOVE,
ADD,
BOTH,
}
impl AlpmHelper {
pub fn new() -> Self {
Self { pkg_list_install: Vec::new(), pkg_list_removal: Vec::new() }
}
pub fn clear(&mut self) {
self.pkg_list_install.clear();
self.pkg_list_removal.clear();
}
pub fn is_empty(&self) -> bool {
self.pkg_list_install.is_empty() && self.pkg_list_removal.is_empty()
}
pub fn do_update(&self) -> AlpmHelperResult {
let mut result = AlpmHelperResult::NOTHING;
if self.pkg_list_install.is_empty() && self.pkg_list_removal.is_empty() {
return result;
}
if !self.pkg_list_removal.is_empty() {
if self.install_apps(&self.pkg_list_removal, false) {
result = AlpmHelperResult::REMOVE;
}
}
if !self.pkg_list_install.is_empty() {
if self.install_apps(&self.pkg_list_install, true) {
if result == AlpmHelperResult::NOTHING {
result = AlpmHelperResult::ADD;
} else {
result = AlpmHelperResult::BOTH;
}
}
}
result
}
pub fn set_package(&mut self, pkg_name: &String, install: bool, installed: bool) {
if self.to_remove(&pkg_name) {
let index = self.pkg_list_removal.iter().position(|x| *x == *pkg_name).unwrap();
self.pkg_list_removal.remove(index);
} else if !install && installed {
self.pkg_list_removal.push(String::from(pkg_name));
}
if self.to_install(&pkg_name) {
let index = self.pkg_list_install.iter().position(|x| *x == *pkg_name).unwrap();
self.pkg_list_install.remove(index);
} else if install && !installed {
self.pkg_list_install.push(String::from(pkg_name));
}
}
pub fn to_install(&self, pkg_name: &String) -> bool {
self.pkg_list_install.contains(&pkg_name)
}
pub fn to_remove(&self, pkg_name: &String) -> bool {
self.pkg_list_removal.contains(&pkg_name)
}
fn install_apps(&self, pkg_list: &Vec<String>, install: bool) -> bool {
let mut install_arg: &str = "";
if pkg_list.is_empty() {
return false;
} else if !install {
install_arg = "-R";
}
println!("pacman {} {:?}", install_arg, pkg_list);
match install {
true => !self.app_installed(&pkg_list[0]),
false => self.app_installed(&pkg_list[0]),
}
}
fn app_installed(&self, pkg_name: &str) -> bool {
let pacman =
pacmanconf::Config::with_opts(None, Some("/etc/pacman.conf"), Some("/")).unwrap();
let alpm = alpm_utils::alpm_with_conf(&pacman).unwrap();
match alpm.localdb().pkg(pkg_name) {
Ok(_) => true,
_ => false,
}
}
}

426
src/application_browser.rs Normal file
View File

@ -0,0 +1,426 @@
use crate::alpm_helper::*;
use crate::config::PKGDATADIR;
use crate::utils;
use gio::prelude::*;
use gtk::prelude::{
BoxExt, ButtonExt, CellRendererExt, CellRendererToggleExt, ComboBoxExt, ContainerExt, GridExt,
GtkListStoreExt, GtkListStoreExtManual, ScrolledWindowExt, ToggleButtonExt, TreeModelExt,
TreeStoreExt, TreeStoreExtManual, TreeViewColumnExt, TreeViewExt, WidgetExt,
};
use once_cell::sync::Lazy;
use serde_json;
use std::fs;
use std::sync::Mutex;
#[derive(Debug)]
pub struct ApplicationBrowser {
pub alpm_handle: alpm::Alpm,
pub alpm_helper: AlpmHelper,
pub filter: bool,
pub app_store: gtk::TreeStore,
pub group_store: gtk::ListStore,
pub group_tofilter: String,
pub groups: serde_json::Value,
pub tree_view: gtk::TreeView,
pub app_browser_box: gtk::Box,
pub button_box: gtk::Box,
pub update_system_btn: gtk::Button,
}
fn new_alpm() -> alpm::Result<alpm::Alpm> {
let pacman = pacmanconf::Config::with_opts(None, Some("/etc/pacman.conf"), Some("/")).unwrap();
let alpm = alpm_utils::alpm_with_conf(&pacman)?;
Ok(alpm)
}
const GROUP: u32 = 0;
const ICON: u32 = 1;
const APPLICATION: u32 = 2;
const DESCRIPTION: u32 = 3;
const ACTIVE: u32 = 4;
const PACKAGE: u32 = 5;
const INSTALLED: u32 = 6;
// static mut G_APP_BROWSER: Rc<Mutex<ApplicationBrowser>> =
// Rc::new(Mutex::new(ApplicationBrowser::new()));
static mut G_APP_BROWSER: Lazy<Mutex<ApplicationBrowser>> = Lazy::new(|| {
let mut app_browser = ApplicationBrowser::new();
app_browser.create_page();
Mutex::new(app_browser)
});
impl ApplicationBrowser {
pub fn new() -> Self {
let app_browser_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
app_browser_box.set_expand(true);
let button_box = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let advanced_button = gtk::ToggleButton::with_label("advanced");
advanced_button.set_tooltip_text(Some("Toggle an extended selection of packages"));
advanced_button.connect_clicked(on_advanced_clicked);
// let download_button = gtk::Button::with_label("download");
// download_button.set_tooltip_text(Some("Download the most recent selection of packages"));
// download_button.connect_clicked(on_download_clicked);
let reset_button = gtk::Button::with_label("reset");
reset_button.set_tooltip_text(Some("Reset your current selections..."));
reset_button.connect_clicked(on_reload_clicked);
let update_system_btn = gtk::Button::with_label("UPDATE SYSTEM");
update_system_btn.set_tooltip_text(Some("Apply your current selections to the system"));
update_system_btn.connect_clicked(on_update_system_clicked);
update_system_btn.set_sensitive(false);
// Group filter
let data =
fs::read_to_string(format!("{}/data/application_utility/default.json", PKGDATADIR))
.expect("Unable to read file");
let groups: serde_json::Value = serde_json::from_str(&data).expect("Unable to parse");
let group_store = load_groups_data(&groups);
let group_combo = utils::create_combo_with_model(&group_store);
group_combo.connect_changed(on_group_filter_changed);
// Packing button box
button_box.pack_start(&advanced_button, false, false, 10);
button_box.pack_start(&group_combo, false, false, 10);
button_box.pack_end(&update_system_btn, false, false, 10);
button_box.pack_end(&reset_button, false, false, 10);
// button_box.pack_end(&download_button, false, false, 10);
app_browser_box.pack_start(&button_box, false, false, 10);
let col_types: [glib::Type; 7] = [
String::static_type(),
String::static_type(),
String::static_type(),
String::static_type(),
bool::static_type(),
String::static_type(),
bool::static_type(),
];
Self {
alpm_handle: new_alpm().unwrap(),
alpm_helper: AlpmHelper::new(),
filter: false,
app_store: gtk::TreeStore::new(&col_types),
group_store,
groups,
group_tofilter: String::from("*"),
tree_view: gtk::TreeView::new(),
app_browser_box,
button_box,
update_system_btn,
}
}
pub fn default_impl() -> &'static Mutex<Self> {
unsafe { &G_APP_BROWSER }
}
fn load_app_data(&mut self) -> usize {
// not use data set for the moment
let mut store_size: usize = 0;
let localdb = self.alpm_handle.localdb();
for group in self.groups.as_array().unwrap() {
if let Some(apps_map) = group.get("apps") {
let g_name = String::from(group["name"].as_str().unwrap());
let g_icon = String::from(group["icon"].as_str().unwrap());
let mut g_desc = String::from(group["description"].as_str().unwrap());
if g_desc.len() < 72 {
g_desc += " ";
}
if self.group_tofilter != "*" && self.group_tofilter != g_name {
continue;
}
if group["filter"].as_array() != None && !self.filter {
continue;
}
let index = self.app_store.insert_with_values(None, None, &[
(GROUP, &None::<String>),
(ICON, &g_icon),
(APPLICATION, &g_name),
(DESCRIPTION, &g_desc),
(ACTIVE, &false),
(PACKAGE, &None::<String>),
(INSTALLED, &false),
]);
store_size += 1;
for app in apps_map.as_array().unwrap() {
let app_name = String::from(app["pkg"].as_str().unwrap());
let mut status = localdb.pkg(app_name).is_ok();
if app["filter"].as_array() != None && !self.filter {
continue;
}
// Restore user checks
if !status
&& self.alpm_helper.to_install(&String::from(app["pkg"].as_str().unwrap()))
{
status = true;
}
if status
&& self.alpm_helper.to_remove(&String::from(app["pkg"].as_str().unwrap()))
{
status = false;
}
self.app_store.insert_with_values(Some(&index), None, &[
(GROUP, &None::<String>),
(ICON, &String::from(app["icon"].as_str().unwrap())),
(APPLICATION, &String::from(app["name"].as_str().unwrap())),
(DESCRIPTION, &String::from(app["description"].as_str().unwrap())),
(ACTIVE, &status),
(PACKAGE, &String::from(app["pkg"].as_str().unwrap())),
(INSTALLED, &status),
]);
}
}
}
store_size
}
pub fn reload_app_data(&mut self, refresh: bool) {
self.alpm_helper.clear();
self.app_store.clear();
if refresh {
self.group_store = load_groups_data(&self.groups);
}
self.load_app_data();
self.tree_view.set_model(Some(&self.app_store));
self.update_system_btn.set_sensitive(!self.alpm_helper.is_empty());
}
fn create_view_tree(&mut self) -> usize {
// setup list store model
let app_store_size = self.load_app_data();
// create a tree view with the model store
self.tree_view = gtk::TreeView::with_model(&self.app_store);
self.tree_view.set_activate_on_single_click(true);
self.tree_view.set_has_tooltip(true);
self.tree_view.connect_query_tooltip(on_query_tooltip_tree_view);
// column model: icon
let icon = gtk::CellRendererPixbuf::new();
let icon_column = create_column("", &icon, "icon_name", ICON);
self.tree_view.append_column(&icon_column);
// column model: group name column
// let group_cell_renderer = gtk::CellRendererText::new();
// let group_column = create_column("Group", &group_cell_renderer, "text", APPLICATION);
// tree_view.append_column(&group_column);
// group_column
// .set_cell_data_func(&group_cell_renderer,
// Some(Box::new(treeview_cell_app_data_function)));
// column model: app name column
let app_cell_renderer = gtk::CellRendererText::new();
let app_column = create_column("Application", &app_cell_renderer, "text", APPLICATION);
// app_column.set_resizable(false);
app_column.set_cell_data_func(
&app_cell_renderer,
Some(Box::new(treeview_cell_app_data_function)),
);
self.tree_view.append_column(&app_column);
// column model: description column
let desc_renderer = gtk::CellRendererText::new();
let desc_column = create_column("Description", &desc_renderer, "text", DESCRIPTION);
desc_column.set_resizable(true);
self.tree_view.append_column(&desc_column);
// column model: install column
let install_renderer = gtk::CellRendererToggle::new();
install_renderer.connect_toggled(on_app_toggle);
let install_column = create_column("Install/Remove", &install_renderer, "active", ACTIVE);
install_column.set_cell_data_func(
&install_renderer,
Some(Box::new(treeview_cell_check_data_function)),
);
install_column.set_resizable(false);
install_column.set_max_width(40);
install_column.set_fixed_width(40);
self.tree_view.append_column(&install_column);
app_store_size
}
pub fn get_page(&self) -> &gtk::Box {
&self.app_browser_box
}
fn create_page(&mut self) {
// create view and app store
let app_store_size = self.create_view_tree();
// create a scrollable window
let app_window = gtk::ScrolledWindow::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
app_window.set_vexpand(true);
app_window.set_policy(gtk::PolicyType::Never, gtk::PolicyType::Automatic);
// add window to tree view
app_window.add(&self.tree_view);
// setup grid
let grid_inter = gtk::Grid::new();
grid_inter.set_column_homogeneous(true);
grid_inter.set_row_homogeneous(true);
// add grid to app browser
self.app_browser_box.add(&grid_inter);
grid_inter.attach(&app_window, 0, 0, 5, app_store_size as i32);
}
pub fn get_alpm_handle(&self) -> &alpm::Alpm {
&self.alpm_handle
}
}
fn treeview_cell_app_data_function(
_column: &gtk::TreeViewColumn,
renderer_cell: &gtk::CellRenderer,
model: &gtk::TreeModel,
iter_a: &gtk::TreeIter,
) {
let value_gobj = model.value(iter_a, INSTALLED as i32);
let value = value_gobj.get::<bool>().unwrap();
if value {
renderer_cell.set_width(600);
} else {
renderer_cell.set_width(400);
}
}
fn treeview_cell_check_data_function(
_column: &gtk::TreeViewColumn,
renderer_cell: &gtk::CellRenderer,
model: &gtk::TreeModel,
iter_a: &gtk::TreeIter,
) {
let value_gobj = model.value(iter_a, GROUP as i32);
let value = value_gobj.get::<&str>().is_ok();
renderer_cell.set_visible(!value);
}
fn on_reload_clicked(_button: &gtk::Button) {
let app_browser = unsafe { &mut G_APP_BROWSER.lock().unwrap() };
app_browser.reload_app_data(false);
}
fn on_group_filter_changed(combo: &gtk::ComboBox) {
let app_browser = unsafe { &mut G_APP_BROWSER.lock().unwrap() };
if let Some(tree_iter) = combo.active_iter() {
let model = combo.model().unwrap();
let group_gobj = model.value(&tree_iter, 0);
let group = group_gobj.get::<&str>().unwrap();
app_browser.group_tofilter = String::from(group);
app_browser.app_store.clear();
app_browser.load_app_data();
app_browser.tree_view.set_model(Some(&app_browser.app_store));
if group != "*" {
app_browser.tree_view.expand_all();
}
}
}
fn on_advanced_clicked(button: &gtk::ToggleButton) {
let app_browser = unsafe { &mut G_APP_BROWSER.lock().unwrap() };
let is_active = button.is_active();
app_browser.filter = is_active;
app_browser.reload_app_data(false);
}
fn on_query_tooltip_tree_view(
treeview: &gtk::TreeView,
x_f: i32,
y_f: i32,
keyboard_tip: bool,
tooltip: &gtk::Tooltip,
) -> bool {
let mut x = x_f;
let mut y = y_f;
let tooltip_context = treeview.tooltip_context(&mut x, &mut y, keyboard_tip);
if let Some((model_tmp, path, iter_a)) = tooltip_context {
let model = model_tmp.unwrap();
let value_gobj = model.value(&iter_a, INSTALLED as i32);
let value = value_gobj.get::<bool>();
if value.is_ok() && value.unwrap() {
let mut msg = String::from("Installed");
let active_gobj = model.value(&iter_a, ACTIVE as i32);
let active = active_gobj.get::<bool>();
if active.is_ok() && !active.unwrap() {
msg.push_str(" , to remove");
}
tooltip.set_markup(Some(msg.as_str()));
treeview.set_tooltip_row(tooltip, &path);
return true;
}
}
false
}
fn on_app_toggle(_cell: &gtk::CellRendererToggle, path: gtk::TreePath) {
let app_browser = unsafe { &mut G_APP_BROWSER.lock().unwrap() };
let app_store = app_browser.app_store.clone();
let iter_a = app_store.iter(&path).unwrap();
let value_gobj = app_store.value(&iter_a, PACKAGE as i32);
// a group has no package attached and we don't install groups
if value_gobj.get::<&str>().is_ok() {
let toggle_a = app_store.value(&iter_a, ACTIVE as i32).get::<bool>().unwrap();
app_store.set(&iter_a, &[(ACTIVE, &!toggle_a)]);
let alpm_handle = app_browser.get_alpm_handle();
let update_system_button = app_browser.update_system_btn.clone();
let localdb = alpm_handle.localdb();
let pkg = app_store.value(&iter_a, PACKAGE as i32).get::<String>().unwrap();
let installed = localdb.pkg(pkg.clone()).is_ok();
// update lists
app_browser.alpm_helper.set_package(&pkg, !toggle_a, installed);
update_system_button.set_sensitive(!app_browser.alpm_helper.is_empty());
}
}
fn on_update_system_clicked(_: &gtk::Button) {
let app_browser = unsafe { &mut G_APP_BROWSER.lock().unwrap() };
if app_browser.alpm_helper.do_update() != AlpmHelperResult::NOTHING {
// reload json for view new apps installed
app_browser.reload_app_data(true);
}
}
fn load_groups_data(groups: &serde_json::Value) -> gtk::ListStore {
// not use data set for the moment
let store = gtk::ListStore::new(&[String::static_type()]);
store.set(&store.append(), &[(0, &String::from("*"))]);
for group in groups.as_array().unwrap() {
let g_name = String::from(group["name"].as_str().unwrap());
store.set(&store.append(), &[(0, &String::from(g_name))]);
}
store
}
fn create_column(
title: &str,
cell: &impl IsA<gtk::CellRenderer>,
attr: &str,
val: u32,
) -> gtk::TreeViewColumn {
let column = gtk::TreeViewColumn::new();
column.set_title(title);
column.pack_start(cell, true);
column.add_attribute(cell, attr, val as i32);
column
}

View File

@ -2,8 +2,11 @@
#![feature(const_str_from_utf8)]
#![allow(non_upper_case_globals)]
mod alpm_helper;
mod application_browser;
mod config;
mod data_types;
mod pages;
mod utils;
use config::{APP_ID, GETTEXT_PACKAGE, LOCALEDIR, PKGDATADIR, PROFILE, VERSION};
@ -12,9 +15,8 @@ use gettextrs::LocaleCategory;
use gtk::{gio, glib, Builder, HeaderBar, Window};
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::fmt::Write as _;
use std::path::Path;
use std::sync::Mutex;
use std::sync::{Arc, Mutex};
use utils::*;
use gio::prelude::*;
@ -23,14 +25,10 @@ use gtk::prelude::*;
use gdk_pixbuf::Pixbuf;
use serde_json::json;
use std::sync::Arc;
use std::{fs, str};
use subprocess::{Exec, Redirection};
use subprocess::Exec;
static mut g_save_json: Lazy<Mutex<serde_json::Value>> = Lazy::new(|| Mutex::new(json!(null)));
static mut g_local_units: Lazy<Mutex<SystemdUnits>> = Lazy::new(|| Mutex::new(SystemdUnits::new()));
static mut g_global_units: Lazy<Mutex<SystemdUnits>> =
Lazy::new(|| Mutex::new(SystemdUnits::new()));
static mut g_hello_window: Option<Arc<HelloWindow>> = None;
@ -71,84 +69,6 @@ fn quick_message(message: &str) {
}
}
fn create_options_section() -> gtk::Box {
let topbox = gtk::Box::new(gtk::Orientation::Vertical, 2);
let box_collection = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let label = gtk::Label::new(None);
label.set_line_wrap(true);
label.set_justify(gtk::Justification::Center);
label.set_text("Tweaks");
let psd_btn = gtk::CheckButton::with_label("Profile-sync-daemon enable");
let systemd_oomd_btn = gtk::CheckButton::with_label("Systemd-oomd enabled");
let apparmor_btn = gtk::CheckButton::with_label("Apparmor enabled");
let ananicy_cpp_btn = gtk::CheckButton::with_label("Ananicy Cpp enabled");
unsafe {
psd_btn.set_data("actionData", "psd.service");
psd_btn.set_data("actionType", "user_service");
systemd_oomd_btn.set_data("actionData", "systemd-oomd.service");
systemd_oomd_btn.set_data("actionType", "service");
apparmor_btn.set_data("actionData", "apparmor.service");
apparmor_btn.set_data("actionType", "service");
ananicy_cpp_btn.set_data("actionData", "ananicy-cpp.service");
ananicy_cpp_btn.set_data("actionType", "service");
}
for btn in &[&psd_btn, &systemd_oomd_btn, &apparmor_btn, &ananicy_cpp_btn] {
unsafe {
let data: &str = *btn.data("actionData").unwrap().as_ptr();
if g_local_units.lock().unwrap().enabled_units.contains(&String::from(data))
|| g_global_units.lock().unwrap().enabled_units.contains(&String::from(data))
{
btn.set_active(true);
}
}
}
psd_btn.connect_clicked(on_servbtn_clicked);
systemd_oomd_btn.connect_clicked(on_servbtn_clicked);
apparmor_btn.connect_clicked(on_servbtn_clicked);
ananicy_cpp_btn.connect_clicked(on_servbtn_clicked);
topbox.pack_start(&label, true, false, 1);
box_collection.pack_start(&psd_btn, true, false, 2);
box_collection.pack_start(&systemd_oomd_btn, true, false, 2);
box_collection.pack_start(&apparmor_btn, true, false, 2);
box_collection.pack_start(&ananicy_cpp_btn, true, false, 2);
box_collection.set_halign(gtk::Align::Fill);
topbox.pack_start(&box_collection, true, false, 1);
topbox.set_hexpand(true);
topbox
}
fn create_apps_section() -> gtk::Box {
let topbox = gtk::Box::new(gtk::Orientation::Vertical, 2);
let box_collection = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let label = gtk::Label::new(None);
label.set_line_wrap(true);
label.set_justify(gtk::Justification::Center);
label.set_text("Applications");
let cachyos_pi = gtk::Button::with_label("CachyOS PackageInstaller");
let cachyos_km = gtk::Button::with_label("CachyOS Kernel Manager");
cachyos_pi.connect_clicked(on_appbtn_clicked);
cachyos_km.connect_clicked(on_appbtn_clicked);
box_collection.pack_start(&cachyos_pi, true, true, 2);
box_collection.pack_start(&cachyos_km, true, true, 2);
topbox.pack_start(&label, true, true, 2);
box_collection.set_halign(gtk::Align::Fill);
topbox.pack_start(&box_collection, true, true, 0);
topbox.set_hexpand(true);
topbox
}
fn show_about_dialog() {
let main_window: Window;
unsafe {
@ -364,50 +284,8 @@ fn build_ui(application: &gtk::Application) {
let install: gtk::Button = builder.object("install").unwrap();
install.set_visible(false);
}
let install: gtk::Button = builder.object("appBrowser").unwrap();
install.set_visible(true);
load_enabled_units();
load_global_enabled_units();
let viewport = gtk::Viewport::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
// let label = gtk::Label::new(None);
// label.set_line_wrap(true);
let image = gtk::Image::from_icon_name(Some("go-previous"), gtk::IconSize::Button);
let back_btn = gtk::Button::new();
back_btn.set_image(Some(&image));
back_btn.set_widget_name("home");
back_btn.connect_clicked(glib::clone!(@weak builder => move |button| {
let name = button.widget_name();
let stack: gtk::Stack = builder.object("stack").unwrap();
stack.set_visible_child_name(&format!("{}page", name));
}));
let options_section_box = create_options_section();
let apps_section_box = create_apps_section();
let grid = gtk::Grid::new();
grid.set_hexpand(true);
grid.set_margin_start(10);
grid.set_margin_end(10);
grid.set_margin_top(5);
grid.set_margin_bottom(5);
grid.attach(&back_btn, 0, 1, 1, 1);
let box_collection = gtk::Box::new(gtk::Orientation::Vertical, 5);
box_collection.pack_start(&options_section_box, true, true, 5);
box_collection.pack_start(&apps_section_box, true, true, 5);
box_collection.set_valign(gtk::Align::Center);
box_collection.set_halign(gtk::Align::Center);
grid.attach(&box_collection, 1, 2, 5, 1);
viewport.add(&grid);
viewport.show_all();
let stack: gtk::Stack = builder.object("stack").unwrap();
let child_name = "appBrowserpage";
stack.add_named(&viewport, child_name);
pages::create_appbrowser_page(&builder);
pages::create_tweaks_page(&builder);
// Show the UI
main_window.show();
@ -575,54 +453,6 @@ fn get_page(name: &str, preferences: &serde_json::Value, save: &serde_json::Valu
fs::read_to_string(filename).unwrap()
}
fn load_enabled_units() {
unsafe {
g_local_units.lock().unwrap().loaded_units.clear();
g_local_units.lock().unwrap().enabled_units.clear();
let mut exec_out = Exec::shell("systemctl list-unit-files -q --no-pager | tr -s \" \"")
.stdout(Redirection::Pipe)
.capture()
.unwrap()
.stdout_str();
exec_out.pop();
let service_list = exec_out.split('\n');
for service in service_list {
let out: Vec<&str> = service.split(' ').collect();
g_local_units.lock().unwrap().loaded_units.push(out[0].to_string());
if out[1] == "enabled" {
g_local_units.lock().unwrap().enabled_units.push(out[0].to_string());
}
}
}
}
fn load_global_enabled_units() {
unsafe {
g_global_units.lock().unwrap().loaded_units.clear();
g_global_units.lock().unwrap().enabled_units.clear();
let mut exec_out =
Exec::shell("systemctl --global list-unit-files -q --no-pager | tr -s \" \"")
.stdout(Redirection::Pipe)
.capture()
.unwrap()
.stdout_str();
exec_out.pop();
let service_list = exec_out.split('\n');
for service in service_list {
let out: Vec<&str> = service.split(' ').collect();
g_global_units.lock().unwrap().loaded_units.push(out[0].to_string());
if out[1] == "enabled" {
g_global_units.lock().unwrap().enabled_units.push(out[0].to_string());
}
}
}
}
/// Handlers
fn on_languages_changed(param: &[glib::Value]) -> Option<glib::Value> {
let widget = param[0].get::<gtk::ComboBox>().unwrap();
@ -652,103 +482,6 @@ fn on_action_clicked(param: &[glib::Value]) -> Option<glib::Value> {
};
}
fn on_servbtn_clicked(button: &gtk::CheckButton) {
// Get action data/type.
let action_type: &str;
let action_data: &str;
unsafe {
action_type = *button.data("actionType").unwrap().as_ptr();
action_data = *button.data("actionData").unwrap().as_ptr();
}
let (user_only, pkexec_only) =
if action_type == "user_service" { ("--user", "--user $(logname)") } else { ("", "") };
let cmd: String;
unsafe {
let local_units = &g_local_units.lock().unwrap().enabled_units;
cmd = if !local_units.contains(&String::from(action_data)) {
format!(
"/sbin/pkexec {} bash -c \"systemctl {} enable --now --force {}\"",
pkexec_only, user_only, action_data
)
} else {
format!(
"/sbin/pkexec {} bash -c \"systemctl {} disable --now {}\"",
pkexec_only, user_only, action_data
)
};
}
// Spawn child process in separate thread.
std::thread::spawn(move || {
Exec::shell(cmd).join().unwrap();
if action_type == "user_service" {
load_global_enabled_units();
} else {
load_enabled_units();
}
});
}
fn on_appbtn_clicked(button: &gtk::Button) {
// Get button label.
let name = button.label().unwrap();
let (binname, is_sudo) = if name == "CachyOS PackageInstaller" {
("cachyos-pi-bin", true)
} else if name == "CachyOS Kernel Manager" {
("cachyos-kernel-manager", false)
} else {
("", false)
};
// Check if executable exists.
let exit_status = Exec::cmd("which").arg(binname).join().unwrap();
if !exit_status.success() {
return;
}
let mut envs = String::new();
for env in glib::listenv() {
if env == "PATH" {
envs += "PATH=/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin ";
continue;
}
let _ = write!(
envs,
"{}=\"{}\" ",
env.to_str().unwrap(),
glib::getenv(&env).unwrap().to_str().unwrap()
);
}
// Get executable path.
let mut exe_path =
Exec::cmd("which").arg(binname).stdout(Redirection::Pipe).capture().unwrap().stdout_str();
exe_path.pop();
let bash_cmd = format!("{} {}", &envs, &exe_path);
// Create context channel.
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
// Spawn child process in separate thread.
std::thread::spawn(move || {
let exit_status = if is_sudo {
Exec::cmd("/sbin/pkexec").arg("bash").arg("-c").arg(bash_cmd).join().unwrap()
} else {
Exec::shell(bash_cmd).join().unwrap()
};
tx.send(format!("Exit status successfully? = {:?}", exit_status.success()))
.expect("Couldn't send data to channel");
});
rx.attach(None, move |text| {
println!("{}", text);
glib::Continue(true)
});
}
fn on_btn_clicked(param: &[glib::Value]) -> Option<glib::Value> {
let widget = param[0].get::<gtk::Button>().unwrap();
let name = widget.widget_name();

388
src/pages.rs Normal file
View File

@ -0,0 +1,388 @@
// extern crate gtk;
use crate::application_browser::ApplicationBrowser;
use crate::data_types::*;
use gtk::{glib, Builder};
use once_cell::sync::Lazy;
use std::fmt::Write as _;
use std::sync::Mutex;
use gtk::prelude::*;
use std::str;
use subprocess::{Exec, Redirection};
// static mut g_app_browser: Lazy<Mutex<ApplicationBrowser>> =
// Lazy::new(|| Mutex::new(ApplicationBrowser::new()));
// pub fn create_appbrowser_page() -> gtk::Box {
// NOTE: we might not even need that here
// let app_browser_box = ApplicationBrowser::default_impl().create_page();
// let app_browser_box = ApplicationBrowser::default_impl().lock().expect("Initialization
// failed!").create_page();
// let app_browser_box = gtk::Box::new(gtk::Orientation::Vertical, 10);
// app_browser_box.set_expand(true);
// let button_box = gtk::Box::new(gtk::Orientation::Horizontal, 10);
// let advanced_button = gtk::Button::with_label("advanced");
// advanced_button.set_tooltip_text(Some("Toggle an extended selection of packages"));
// advanced_button.connect_clicked(on_advanced_clicked);
// let download_button = gtk::Button::with_label("download");
// download_button.set_tooltip_text(Some("Download the most recent selection of packages"));
// download_button.connect_clicked(on_download_clicked);
// let reset_button = gtk::Button::with_label("reset");
// reset_button.set_tooltip_text(Some("Reset your current selections..."));
// reset_button.connect_clicked(on_reload_clicked);
// let update_system_button = unsafe { g_app_browser.lock().unwrap().update_system_btn };
// Group filter
// let data = fs::read_to_string(format!("{}/data/application_utility/default.json",
// PKGDATADIR)) .expect("Unable to read file");
// let groups: serde_json::Value = serde_json::from_str(&data).expect("Unable to parse");
// let group_store = load_groups_data(&groups);
// let group_combo = utils::create_combo_with_model(&group_store);
// Packing button box
// button_box.pack_start(&advanced_button, false, false, 10);
// button_box.pack_start(&group_combo, false, false, 10);
// button_box.pack_end(&update_system_button, false, false, 10);
// button_box.pack_end(&reset_button, false, false, 10);
// button_box.pack_end(&download_button, false, false, 10);
// app_browser_box.pack_start(&button_box, false, false, 10);
// create view and app store
// let (tree_view, app_store_size) = create_view_tree(&groups);
// create a scrollable window
// let app_window = gtk::ScrolledWindow::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
// app_window.set_vexpand(true);
// app_window.set_policy(gtk::PolicyType::Never, gtk::PolicyType::Automatic);
// add window to tree view
// app_window.add(&tree_view);
// // setup grid
// let grid_inter = gtk::Grid::new();
// grid_inter.set_column_homogeneous(true);
// grid_inter.set_row_homogeneous(true);
// // add grid to app browser
// app_browser_box.add(&grid_inter);
// grid_inter.attach(&app_window, 0, 0, 5, app_store_size as i32);
//
// app_browser_box
// gtk::Box::new(gtk::Orientation::Vertical, 10)
//}
static mut g_local_units: Lazy<Mutex<SystemdUnits>> = Lazy::new(|| Mutex::new(SystemdUnits::new()));
static mut g_global_units: Lazy<Mutex<SystemdUnits>> =
Lazy::new(|| Mutex::new(SystemdUnits::new()));
fn create_options_section() -> gtk::Box {
let topbox = gtk::Box::new(gtk::Orientation::Vertical, 2);
let box_collection = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let label = gtk::Label::new(None);
label.set_line_wrap(true);
label.set_justify(gtk::Justification::Center);
label.set_text("Tweaks");
let psd_btn = gtk::CheckButton::with_label("Profile-sync-daemon enable");
let systemd_oomd_btn = gtk::CheckButton::with_label("Systemd-oomd enabled");
let apparmor_btn = gtk::CheckButton::with_label("Apparmor enabled");
let ananicy_cpp_btn = gtk::CheckButton::with_label("Ananicy Cpp enabled");
unsafe {
psd_btn.set_data("actionData", "psd.service");
psd_btn.set_data("actionType", "user_service");
systemd_oomd_btn.set_data("actionData", "systemd-oomd.service");
systemd_oomd_btn.set_data("actionType", "service");
apparmor_btn.set_data("actionData", "apparmor.service");
apparmor_btn.set_data("actionType", "service");
ananicy_cpp_btn.set_data("actionData", "ananicy-cpp.service");
ananicy_cpp_btn.set_data("actionType", "service");
}
for btn in &[&psd_btn, &systemd_oomd_btn, &apparmor_btn, &ananicy_cpp_btn] {
unsafe {
let data: &str = *btn.data("actionData").unwrap().as_ptr();
if g_local_units.lock().unwrap().enabled_units.contains(&String::from(data))
|| g_global_units.lock().unwrap().enabled_units.contains(&String::from(data))
{
btn.set_active(true);
}
}
}
psd_btn.connect_clicked(on_servbtn_clicked);
systemd_oomd_btn.connect_clicked(on_servbtn_clicked);
apparmor_btn.connect_clicked(on_servbtn_clicked);
ananicy_cpp_btn.connect_clicked(on_servbtn_clicked);
topbox.pack_start(&label, true, false, 1);
box_collection.pack_start(&psd_btn, true, false, 2);
box_collection.pack_start(&systemd_oomd_btn, true, false, 2);
box_collection.pack_start(&apparmor_btn, true, false, 2);
box_collection.pack_start(&ananicy_cpp_btn, true, false, 2);
box_collection.set_halign(gtk::Align::Fill);
topbox.pack_start(&box_collection, true, false, 1);
topbox.set_hexpand(true);
topbox
}
fn create_apps_section() -> gtk::Box {
let topbox = gtk::Box::new(gtk::Orientation::Vertical, 2);
let box_collection = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let label = gtk::Label::new(None);
label.set_line_wrap(true);
label.set_justify(gtk::Justification::Center);
label.set_text("Applications");
let cachyos_pi = gtk::Button::with_label("CachyOS PackageInstaller");
let cachyos_km = gtk::Button::with_label("CachyOS Kernel Manager");
cachyos_pi.connect_clicked(on_appbtn_clicked);
cachyos_km.connect_clicked(on_appbtn_clicked);
box_collection.pack_start(&cachyos_pi, true, true, 2);
box_collection.pack_start(&cachyos_km, true, true, 2);
topbox.pack_start(&label, true, true, 2);
box_collection.set_halign(gtk::Align::Fill);
topbox.pack_start(&box_collection, true, true, 0);
topbox.set_hexpand(true);
topbox
}
fn load_enabled_units() {
unsafe {
g_local_units.lock().unwrap().loaded_units.clear();
g_local_units.lock().unwrap().enabled_units.clear();
let mut exec_out = Exec::shell("systemctl list-unit-files -q --no-pager | tr -s \" \"")
.stdout(Redirection::Pipe)
.capture()
.unwrap()
.stdout_str();
exec_out.pop();
let service_list = exec_out.split('\n');
for service in service_list {
let out: Vec<&str> = service.split(' ').collect();
g_local_units.lock().unwrap().loaded_units.push(out[0].to_string());
if out[1] == "enabled" {
g_local_units.lock().unwrap().enabled_units.push(out[0].to_string());
}
}
}
}
fn load_global_enabled_units() {
unsafe {
g_global_units.lock().unwrap().loaded_units.clear();
g_global_units.lock().unwrap().enabled_units.clear();
let mut exec_out =
Exec::shell("systemctl --global list-unit-files -q --no-pager | tr -s \" \"")
.stdout(Redirection::Pipe)
.capture()
.unwrap()
.stdout_str();
exec_out.pop();
let service_list = exec_out.split('\n');
for service in service_list {
let out: Vec<&str> = service.split(' ').collect();
g_global_units.lock().unwrap().loaded_units.push(out[0].to_string());
if out[1] == "enabled" {
g_global_units.lock().unwrap().enabled_units.push(out[0].to_string());
}
}
}
}
pub fn create_tweaks_page(builder: &Builder) {
let install: gtk::Button = builder.object("tweaksBrowser").unwrap();
install.set_visible(true);
load_enabled_units();
load_global_enabled_units();
let viewport = gtk::Viewport::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
// let label = gtk::Label::new(None);
// label.set_line_wrap(true);
let image = gtk::Image::from_icon_name(Some("go-previous"), gtk::IconSize::Button);
let back_btn = gtk::Button::new();
back_btn.set_image(Some(&image));
back_btn.set_widget_name("home");
back_btn.connect_clicked(glib::clone!(@weak builder => move |button| {
let name = button.widget_name();
let stack: gtk::Stack = builder.object("stack").unwrap();
stack.set_visible_child_name(&format!("{}page", name));
}));
let options_section_box = create_options_section();
let apps_section_box = create_apps_section();
let grid = gtk::Grid::new();
grid.set_hexpand(true);
grid.set_margin_start(10);
grid.set_margin_end(10);
grid.set_margin_top(5);
grid.set_margin_bottom(5);
grid.attach(&back_btn, 0, 1, 1, 1);
let box_collection = gtk::Box::new(gtk::Orientation::Vertical, 5);
box_collection.pack_start(&options_section_box, true, true, 5);
box_collection.pack_start(&apps_section_box, true, true, 5);
box_collection.set_valign(gtk::Align::Center);
box_collection.set_halign(gtk::Align::Center);
grid.attach(&box_collection, 1, 2, 5, 1);
viewport.add(&grid);
viewport.show_all();
let stack: gtk::Stack = builder.object("stack").unwrap();
let child_name = "tweaksBrowserpage";
stack.add_named(&viewport, child_name);
}
pub fn create_appbrowser_page(builder: &Builder) {
let install: gtk::Button = builder.object("appBrowser").unwrap();
install.set_visible(true);
let viewport = gtk::Viewport::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
// let label = gtk::Label::new(None);
// label.set_line_wrap(true);
let image = gtk::Image::from_icon_name(Some("go-previous"), gtk::IconSize::Button);
let back_btn = gtk::Button::new();
back_btn.set_image(Some(&image));
back_btn.set_widget_name("home");
back_btn.connect_clicked(glib::clone!(@weak builder => move |button| {
let name = button.widget_name();
let stack: gtk::Stack = builder.object("stack").unwrap();
stack.set_visible_child_name(&format!("{}page", name));
}));
let grid = gtk::Grid::new();
grid.set_hexpand(true);
grid.set_margin_start(10);
grid.set_margin_end(10);
grid.set_margin_top(5);
grid.set_margin_bottom(5);
grid.attach(&back_btn, 0, 1, 1, 1);
let app_browser_ref = ApplicationBrowser::default_impl().lock().unwrap();
let app_browser_box = app_browser_ref.get_page();
grid.attach(app_browser_box, 0, 2, 1, 1);
// Add grid to the viewport
// NOTE: we might eliminate that?
viewport.add(&grid);
viewport.show_all();
let stack: gtk::Stack = builder.object("stack").unwrap();
let child_name = "appBrowserpage";
stack.add_named(&viewport, child_name);
}
fn on_servbtn_clicked(button: &gtk::CheckButton) {
// Get action data/type.
let action_type: &str;
let action_data: &str;
unsafe {
action_type = *button.data("actionType").unwrap().as_ptr();
action_data = *button.data("actionData").unwrap().as_ptr();
}
let (user_only, pkexec_only) =
if action_type == "user_service" { ("--user", "--user $(logname)") } else { ("", "") };
let cmd: String;
unsafe {
let local_units = &g_local_units.lock().unwrap().enabled_units;
cmd = if !local_units.contains(&String::from(action_data)) {
format!(
"/sbin/pkexec {} bash -c \"systemctl {} enable --now --force {}\"",
pkexec_only, user_only, action_data
)
} else {
format!(
"/sbin/pkexec {} bash -c \"systemctl {} disable --now {}\"",
pkexec_only, user_only, action_data
)
};
}
// Spawn child process in separate thread.
std::thread::spawn(move || {
Exec::shell(cmd).join().unwrap();
if action_type == "user_service" {
load_global_enabled_units();
} else {
load_enabled_units();
}
});
}
fn on_appbtn_clicked(button: &gtk::Button) {
// Get button label.
let name = button.label().unwrap();
let (binname, is_sudo) = if name == "CachyOS PackageInstaller" {
("cachyos-pi-bin", true)
} else if name == "CachyOS Kernel Manager" {
("cachyos-kernel-manager", false)
} else {
("", false)
};
// Check if executable exists.
let exit_status = Exec::cmd("which").arg(binname).join().unwrap();
if !exit_status.success() {
return;
}
let mut envs = String::new();
for env in glib::listenv() {
if env == "PATH" {
envs += "PATH=/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin ";
continue;
}
let _ = write!(
envs,
"{}=\"{}\" ",
env.to_str().unwrap(),
glib::getenv(&env).unwrap().to_str().unwrap()
);
}
// Get executable path.
let mut exe_path =
Exec::cmd("which").arg(binname).stdout(Redirection::Pipe).capture().unwrap().stdout_str();
exe_path.pop();
let bash_cmd = format!("{} {}", &envs, &exe_path);
// Create context channel.
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
// Spawn child process in separate thread.
std::thread::spawn(move || {
let exit_status = if is_sudo {
Exec::cmd("/sbin/pkexec").arg("bash").arg("-c").arg(bash_cmd).join().unwrap()
} else {
Exec::shell(bash_cmd).join().unwrap()
};
tx.send(format!("Exit status successfully? = {:?}", exit_status.success()))
.expect("Couldn't send data to channel");
});
rx.attach(None, move |text| {
println!("{}", text);
glib::Continue(true)
});
}

View File

@ -1,3 +1,4 @@
use gtk::prelude::*;
use std::fs::File;
use std::{fs, slice, str};
@ -54,6 +55,16 @@ pub fn check_regular_file(path: &str) -> bool {
}
}
pub fn create_combo_with_model(group_store: &gtk::ListStore) -> gtk::ComboBox {
let group_combo = gtk::ComboBox::with_model(group_store);
let combo_renderer = gtk::CellRendererText::new();
group_combo.pack_start(&combo_renderer, true);
group_combo.add_attribute(&combo_renderer, "text", 0);
group_combo.set_active(Some(0));
group_combo
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -425,10 +425,27 @@ We, the CachyOS Developers, hope that you will enjoy using CachyOS as much as we
<property name="homogeneous">True</property>
<child>
<object class="GtkButton" id="appBrowser">
<property name="label" translatable="yes">Apps/Tweaks</property>
<property name="label" translatable="yes">Install Apps</property>
<property name="name">appBrowser</property>
<property name="can-focus">False</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Install Apps</property>
<property name="margin-left">15</property>
<property name="margin-right">15</property>
<signal name="clicked" handler="on_btn_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="tweaksBrowser">
<property name="label" translatable="yes">Apps/Tweaks</property>
<property name="name">tweaksBrowser</property>
<property name="can-focus">False</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Apps/Tweaks</property>
<property name="margin-left">15</property>
<property name="margin-right">15</property>