65 Commits

Author SHA1 Message Date
piotr
c0eb0965a8 update README.md 2024-10-26 00:58:25 +02:00
piotr
c940139fd9 add default styling for #math-result label #134 2024-10-26 00:43:34 +02:00
piotr
10b1b2a1e6 Merge remote-tracking branch 'origin/main' 2024-10-26 00:38:44 +02:00
piotr
b4b7d3486a add CSS ID to math result label #134 2024-10-26 00:38:20 +02:00
Piotr Miller
61a5595d84 Merge pull request #132 from msmafra/main
Update README.md
2024-10-15 11:27:46 +02:00
Marcelo dos Santos Mafra
cb44c7215f Update README.md
nwg-drawer seems to look in its own folder ~/.config/nwg-drawer for preferred-apps.json and excluded-dirs and not nwg-panel's folder
2024-10-11 17:59:30 -03:00
piotr
e7fafebf55 bump to 0.5.2 2024-10-09 02:12:19 +02:00
piotr
702cbb1c5c update golang.org/x/sys 2024-10-09 02:07:51 +02:00
piotr
7de2854041 set gtk-layer-shell namespace 2024-10-09 02:06:30 +02:00
Piotr Miller
be6b71673d Merge pull request #131 from nwg-piotr/calc
add a simple calculator in the search box
2024-10-03 01:20:01 +02:00
piotr
9c48c585b7 add rounded corners 2024-10-03 01:10:35 +02:00
piotr
3977c4fc7c go -> 1.23 2024-10-03 01:05:14 +02:00
piotr
46a526ced8 close result win on btn release 2024-10-03 01:04:53 +02:00
piotr
12da77fbfc update README.md 2024-10-03 00:48:23 +02:00
piotr
484c6ccb29 add comments 2024-10-03 00:33:54 +02:00
piotr
7aac5a4f52 add expression calculator 2024-09-30 22:09:56 +02:00
Piotr Miller
44e4c95900 Merge pull request #130 from nwg-piotr/backports
Merge pull request #129 from nwg-piotr/main
2024-09-22 14:05:20 +02:00
Piotr Miller
86a3398bc3 Merge pull request #129 from nwg-piotr/main
A bunch of fixes
2024-09-22 14:04:33 +02:00
piotr
c4a268332a bump to 0.5.0 2024-09-22 13:57:01 +02:00
piotr
e77d189f4a println -> log.Debug 2024-09-22 13:54:32 +02:00
piotr
e3cb0af2bd println -> log.Debug 2024-09-22 13:51:46 +02:00
piotr
1015f4c48f fix hyprclt & mapping output->monitor 2024-09-22 13:44:02 +02:00
piotr
86c92328cd update dependencies 2024-09-22 13:29:07 +02:00
piotr
4afa9f1aa3 add riverctl spawn 2024-09-22 13:26:18 +02:00
Piotr Miller
0bce8cf971 Merge pull request #127 from mattkae/main
Update gotk3 to the latest version to avoid build failures
2024-08-11 22:20:16 +02:00
Matthew Kosarek
e6a1130524 Update gotk3 to the latest version to avoid build failures 2024-08-11 15:59:14 -04:00
Piotr Miller
433c05ca1e Merge pull request #125 from nwg-piotr/icon-theme
update README.md
2024-06-29 20:20:34 +02:00
piotr
cd06a981cb update README.md 2024-06-29 20:19:12 +02:00
Piotr Miller
0f6f92433e Merge pull request #124 from nwg-piotr/icon-theme
Add the `-pbuseicontheme` flagIcon theme
2024-06-29 03:01:20 +02:00
piotr
3ffd09b5d7 add more desc 2024-06-29 02:40:01 +02:00
piotr
071841da46 bump to 0.4.9 2024-06-29 02:38:43 +02:00
piotr
c7a0b564c0 improve description 2024-06-29 02:34:41 +02:00
piotr
6862711010 add pbUseIconTheme flag #122 2024-06-29 02:32:27 +02:00
piotr
6d671062df change logging level 2024-06-17 02:16:52 +02:00
piotr
9aebff5d98 fix typos 2024-06-17 02:10:03 +02:00
piotr
051590ce4b revert gotk3 to v0.6.3 2024-06-17 02:03:39 +02:00
piotr
16be4ff00d bump to 0.4.8 2024-06-17 02:01:20 +02:00
piotr
9f0f708cae Merge remote-tracking branch 'origin/main' 2024-06-17 01:53:43 +02:00
piotr
4e04e7a4f4 update dependencies 2024-06-17 01:53:25 +02:00
Piotr Miller
85a0b87024 Merge pull request #121 from RRRRRm/open_close
Add -open and -close options
2024-06-17 01:52:21 +02:00
RRRRRm
6649dcefdb Correct the default terminal 2024-06-17 00:31:52 +01:00
Zijia Xiong
e501038c64 Merge branch 'open_close' of github.com:RRRRRm/nwg-drawer into open_close 2024-06-16 19:04:31 +01:00
Zijia Xiong
008f31278d Fix logic about receive signals 2024-06-16 19:01:26 +01:00
RRRRRm
69e3b06b39 Update README.md,
Add some information about new flags `-open` and `-close`
2024-06-16 18:35:01 +01:00
Zijia Xiong
99765402d7 Change usage of SIGINT to SIGRTMIN+3 2024-06-14 00:48:10 +01:00
Zijia Xiong
3fb70fc0cb Fix bug of -open and -close 2024-06-13 23:30:58 +01:00
Zijia Xiong
04a04c1417 Add -close and -open options. 2024-06-13 19:09:33 +01:00
Piotr Miller
a54a56c4b9 Update FUNDING.yml 2024-02-16 13:17:28 +01:00
piotr
0cd48d1631 unhardcode desktop-directories path 2024-02-08 03:12:17 +01:00
piotr
943ca39055 update README.md 2024-02-08 02:51:16 +01:00
piotr
42b279ee02 update README.md 2024-02-08 02:49:57 +01:00
Piotr Miller
42f15ec2cb Merge pull request #116 from nwg-piotr/data
un-hardcode data dir
2024-02-08 02:35:16 +01:00
piotr
936f793be4 update README.md 2024-02-08 02:28:21 +01:00
piotr
b59058c5c2 focus 1st power button on Tab key 2024-02-08 02:23:34 +01:00
piotr
3ea4a7824c bump to 0.4.7 2024-02-08 01:32:37 +01:00
piotr
701d4e62f2 unhardcode data dir again #115 2024-02-08 01:31:22 +01:00
piotr
d4996af84a unhardcode data dir #115 2024-02-08 01:20:03 +01:00
piotr
71ce3b5d75 Merge remote-tracking branch 'origin/main' 2024-02-06 03:40:14 +01:00
piotr
b056b4c436 bump to 0.4.6 2024-02-06 03:39:35 +01:00
Piotr Miller
42b55d41df Merge pull request #113 from nwg-piotr/touch
Don't launch if the window has been scrolled
2024-02-06 03:38:09 +01:00
piotr
ec0ce767d9 get proper adjustment 2024-02-06 03:35:29 +01:00
piotr
ec3caa02d2 add comments #110 2024-02-06 03:33:12 +01:00
piotr
6b39eba14c check if window scrolled #110 2024-02-06 03:21:42 +01:00
piotr
631b651eed bump to 0.4.5 2024-02-05 01:02:34 +01:00
piotr
df644f4196 gotk3 -> 0.6.3 2024-02-05 01:01:30 +01:00
9 changed files with 334 additions and 114 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1 +1,2 @@
github: nwg-piotr github: nwg-piotr
liberapay: nwg

View File

@@ -9,7 +9,7 @@ get:
go get github.com/fsnotify/fsnotify go get github.com/fsnotify/fsnotify
build: build:
go build -o bin/nwg-drawer . go build -v -o bin/nwg-drawer .
install: install:
-pkill -f nwg-drawer -pkill -f nwg-drawer

View File

@@ -13,9 +13,9 @@ and for files in XDG user directories. The grid view may also be filtered by cat
You may pin applications by right-clicking them. Pinned items will appear above the application grid. Right-click You may pin applications by right-clicking them. Pinned items will appear above the application grid. Right-click
a pinned item to unpin it. The pinned items cache is shared with [nwg-menu](https://github.com/nwg-piotr/nwg-menu). a pinned item to unpin it. The pinned items cache is shared with [nwg-menu](https://github.com/nwg-piotr/nwg-menu).
Below the grid there is the power bar - a row of buttons to lock the screen, exit the compositor, reboot, suspend and power Below the grid there is the **power bar** - a row of buttons to lock the screen, exit the compositor, reboot, suspend
the machine off. For each button to appear, you need to provide a corresponding command. See "Command line arguments" and power the machine off. For each button to appear, you need to provide a corresponding command. See "Command line
below. arguments" below. If the power bar is present, pressing **Tab** will move focus to its first button.
<img src="https://github.com/nwg-piotr/nwg-drawer/assets/20579136/8f4eacb4-5395-4350-889b-a9037aa34f08" width=640 alt="screenshot"><br> <img src="https://github.com/nwg-piotr/nwg-drawer/assets/20579136/8f4eacb4-5395-4350-889b-a9037aa34f08" width=640 alt="screenshot"><br>
@@ -27,7 +27,7 @@ To close the window w/o running a program, you may use the `Esc` key, or right-c
### Dependencies ### Dependencies
- go >=1.20 (just to build) - go
- gtk3 - gtk3
- gtk-layer-shell - gtk-layer-shell
- xdg-utils - xdg-utils
@@ -51,9 +51,11 @@ confirmed to work well with the program. Also see **Files** below.
```text ```text
$ nwg-drawer -h $ nwg-drawer -h
Usage of /tmp/go-build3511850078/b001/exe/nwg-drawer: Usage of nwg-drawer:
-c uint -c uint
number of Columns (default 6) number of Columns (default 6)
-close
close drawer of existing instance
-d Turn on Debug messages -d Turn on Debug messages
-fm string -fm string
File Manager (default "thunar") File Manager (default "thunar")
@@ -86,6 +88,8 @@ Usage of /tmp/go-build3511850078/b001/exe/nwg-drawer:
Disable file search Disable file search
-o string -o string
name of the Output to display the drawer on (sway & Hyprland only) name of the Output to display the drawer on (sway & Hyprland only)
-open
open drawer of existing instance
-ovl -ovl
use OVerLay layer use OVerLay layer
-pbexit string -pbexit string
@@ -97,9 +101,11 @@ Usage of /tmp/go-build3511850078/b001/exe/nwg-drawer:
-pbreboot string -pbreboot string
command for the Reboot power bar icon command for the Reboot power bar icon
-pbsize int -pbsize int
power bar icon size (default 64) power bar icon size (only works w/ built-in icons) (default 64)
-pbsleep string -pbsleep string
command for the sleep power bar icon command for the sleep power bar icon
-pbuseicontheme
use icon theme instead of built-in icons in power bar
-r Leave the program resident in memory -r Leave the program resident in memory
-s string -s string
Styling: css file name (default "drawer.css") Styling: css file name (default "drawer.css")
@@ -137,6 +143,15 @@ this should be a little bit faster.
Running a resident instance should speed up use of the drawer significantly. Pay attention to the fact, that you Running a resident instance should speed up use of the drawer significantly. Pay attention to the fact, that you
need to `pkill -f nwg-drawer` and reload the compositor to apply any new arguments! need to `pkill -f nwg-drawer` and reload the compositor to apply any new arguments!
If you want to explicitly specify commands to open and close the resident instance, which can be helpful for touchpad gestures, please use the `-open` and `-close` parameters. Similarly, some signals can also be use: pkill -USR2 nwg-drawer to open and pkill -SIGRTMIN+3 nwg-drawer to close.
For a MacOS-style three-finger pinch:
```text
bindgesture pinch:4:inward exec pkill -SIGUSR2 nwg-drawer
bindgesture pinch:4:outward exec pkill -SIGRTMIN+3 nwg-drawer
```
## Logging ## Logging
In case you encounter an issue, you may need debug messages. If you use the resident instance, you'll see nothing In case you encounter an issue, you may need debug messages. If you use the resident instance, you'll see nothing
@@ -157,7 +172,7 @@ Edit `~/.config/nwg-drawer/drawer.css` to your taste.
When the search phrase is at least 3 characters long, your XDG user directories are being searched. When the search phrase is at least 3 characters long, your XDG user directories are being searched.
Use the **left mouse button** to open a file with the `xdg-open` command. As configuring file associations for it is Use the **left mouse button** to open a file with the `xdg-open` command. As configuring file associations for it is
PITA, you may override them, by creating the `~/.config/nwg-panel/preferred-apps.json` file with your own definitions. PITA, you may override them, by creating the `~/.config/nwg-drawer/preferred-apps.json` file with your own definitions.
### Sample `preferred-apps.json` file content ### Sample `preferred-apps.json` file content
@@ -184,13 +199,29 @@ I've noy yet tried other file managers.
### File search exclusions ### File search exclusions
You may want to exclude some paths inside your XDG user directories from searching. If so, define exclusions in the You may want to exclude some paths inside your XDG user directories from searching. If so, define exclusions in the
`~/.config/nwg-panel/excluded-dirs` file, e.g. like this: `~/.config/nwg-drawer/excluded-dirs` file, e.g. like this:
```text ```text
# exclude all paths containing 'node_modules' # exclude all paths containing 'node_modules'
node_modules node_modules
``` ```
### Calculations in the search box
If the search box is not empty, and you press Enter, the search box content will be evaluated as an arithmetic operation.
If the result is not an error, it will be displayed in a small window, and copied to the clipboard with wl-copy.
Press any key to close the window.
You may change the result label styling e.g. like this:
```css
/* math operation result label */
#math-label {
font-weight: bold;
font-size: 16px
}
```
## Credits ## Credits
This program uses some great libraries: This program uses some great libraries:
@@ -202,3 +233,4 @@ Copyright (c) 2015-2018 gotk3 contributors
- [go-singleinstance](github.com/allan-simon/go-singleinstance) Copyright (c) 2015 Allan Simon - [go-singleinstance](github.com/allan-simon/go-singleinstance) Copyright (c) 2015 Allan Simon
- [logrus](https://github.com/sirupsen/logrus) Copyright (c) 2014 Simon Eskildsen - [logrus](https://github.com/sirupsen/logrus) Copyright (c) 2014 Simon Eskildsen
- [fsnotify](https://github.com/fsnotify/fsnotify) Copyright (c) 2012-2019 fsnotify Authors - [fsnotify](https://github.com/fsnotify/fsnotify) Copyright (c) 2012-2019 fsnotify Authors
- [expr](https://github.com/expr-lang/expr) Copyright (c) 2018 Anton Medvedev

View File

@@ -1,5 +1,5 @@
window { window {
background-color: rgba (36, 47, 79, 0.95); background-color: rgba(36, 47, 79, 0.95);
color: #eeeeee color: #eeeeee
} }
@@ -32,3 +32,9 @@ button:hover {
border: 1px dotted gray; border: 1px dotted gray;
border-radius: 15px border-radius: 15px
} }
/* math operation result label */
#math-label {
font-weight: bold;
font-size: 16px
}

11
go.mod
View File

@@ -1,19 +1,20 @@
module github.com/nwg-piotr/nwg-drawer module github.com/nwg-piotr/nwg-drawer
go 1.21 go 1.23
require ( require (
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37 github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37
github.com/dlasky/gotk3-layershell v0.0.0-20230802002603-b0c42cd8474f github.com/dlasky/gotk3-layershell v0.0.0-20240515133811-5c5115f0d774
github.com/fsnotify/fsnotify v1.7.0 github.com/fsnotify/fsnotify v1.7.0
github.com/gotk3/gotk3 v0.6.2 github.com/gotk3/gotk3 v0.6.5-0.20240618185848-ff349ae13f56
github.com/joshuarubin/go-sway v1.2.0 github.com/joshuarubin/go-sway v1.2.0
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
) )
require ( require (
github.com/expr-lang/expr v1.16.9 // indirect
github.com/joshuarubin/lifecycle v1.1.4 // indirect github.com/joshuarubin/lifecycle v1.1.4 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/sync v0.6.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.16.0 // indirect golang.org/x/sys v0.26.0 // indirect
) )

38
go.sum
View File

@@ -3,17 +3,15 @@ github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37/go.m
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlasky/gotk3-layershell v0.0.0-20221218201547-1f6674a3f872 h1:16qcNl+UgbvudN7wPv+zq4mmDSYJWdLv5jbVhS7+OVI= github.com/dlasky/gotk3-layershell v0.0.0-20240515133811-5c5115f0d774 h1:o87OVL4olQBlVwN3+NSVQpS6gj9FWUYtxOfHXWZigUE=
github.com/dlasky/gotk3-layershell v0.0.0-20221218201547-1f6674a3f872/go.mod h1:JHLx2Wz4mAPVwn4PFhC69ydwyHP4A3wQvlg7HKVVc1U= github.com/dlasky/gotk3-layershell v0.0.0-20240515133811-5c5115f0d774/go.mod h1:JHLx2Wz4mAPVwn4PFhC69ydwyHP4A3wQvlg7HKVVc1U=
github.com/dlasky/gotk3-layershell v0.0.0-20230802002603-b0c42cd8474f h1:qDnUQAD7tVX/gnL6uSgouzfGNA4xXH+B/fd6Ko19GgM= github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
github.com/dlasky/gotk3-layershell v0.0.0-20230802002603-b0c42cd8474f/go.mod h1:JHLx2Wz4mAPVwn4PFhC69ydwyHP4A3wQvlg7HKVVc1U= github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gotk3/gotk3 v0.6.1/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q= github.com/gotk3/gotk3 v0.6.1/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
github.com/gotk3/gotk3 v0.6.2 h1:sx/PjaKfKULJPTPq8p2kn2ZbcNFxpOJqi4VLzMbEOO8= github.com/gotk3/gotk3 v0.6.5-0.20240618185848-ff349ae13f56 h1:eR+xxC8qqKuPMTucZqaklBxLIT7/4L7dzhlwKMrDbj8=
github.com/gotk3/gotk3 v0.6.2/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q= github.com/gotk3/gotk3 v0.6.5-0.20240618185848-ff349ae13f56/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
github.com/joshuarubin/go-sway v1.2.0 h1:t3eqW504//uj9PDwFf0+IVfkD+WoOGaDX5gYIe0BHyM= github.com/joshuarubin/go-sway v1.2.0 h1:t3eqW504//uj9PDwFf0+IVfkD+WoOGaDX5gYIe0BHyM=
github.com/joshuarubin/go-sway v1.2.0/go.mod h1:qcDd6f25vJ0++wICwA1BainIcRC67p2Mb4lsrZ0k3/k= github.com/joshuarubin/go-sway v1.2.0/go.mod h1:qcDd6f25vJ0++wICwA1BainIcRC67p2Mb4lsrZ0k3/k=
github.com/joshuarubin/lifecycle v1.0.0/go.mod h1:sRy++ATvR9Ee21tkRdFkQeywAWvDsue66V70K0Dnl54= github.com/joshuarubin/lifecycle v1.0.0/go.mod h1:sRy++ATvR9Ee21tkRdFkQeywAWvDsue66V70K0Dnl54=
@@ -32,20 +30,18 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

180
main.go
View File

@@ -3,6 +3,7 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"github.com/expr-lang/expr"
"os" "os"
"os/signal" "os/signal"
"path" "path"
@@ -21,17 +22,20 @@ import (
"github.com/gotk3/gotk3/gtk" "github.com/gotk3/gotk3/gtk"
) )
const version = "0.4.4" const version = "0.5.2"
var ( var (
appDirs []string appDirs []string
configDirectory string configDirectory string
dataDirectory string
pinnedFile string pinnedFile string
pinned []string pinned []string
id2entry map[string]desktopEntry id2entry map[string]desktopEntry
preferredApps map[string]interface{} preferredApps map[string]interface{}
exclusions []string exclusions []string
hyprlandMonitors []monitor hyprlandMonitors []monitor
beenScrolled bool
firstPowerBtn *gtk.Button
) )
var categoryNames = [...]string{ var categoryNames = [...]string{
@@ -152,6 +156,8 @@ var targetOutput = flag.String("o", "", "name of the Output to display the drawe
var displayVersion = flag.Bool("v", false, "display Version information") var displayVersion = flag.Bool("v", false, "display Version information")
var keyboard = flag.Bool("k", false, "set GTK layer shell Keyboard interactivity to 'on-demand' mode") var keyboard = flag.Bool("k", false, "set GTK layer shell Keyboard interactivity to 'on-demand' mode")
var overlay = flag.Bool("ovl", false, "use OVerLay layer") var overlay = flag.Bool("ovl", false, "use OVerLay layer")
var flagDrawerOpen = flag.Bool("open", false, "open drawer of existing instance")
var flagDrawerClose = flag.Bool("close", false, "close drawer of existing instance")
var gtkTheme = flag.String("g", "", "GTK theme name") var gtkTheme = flag.String("g", "", "GTK theme name")
var gtkIconTheme = flag.String("i", "", "GTK icon theme name") var gtkIconTheme = flag.String("i", "", "GTK icon theme name")
var iconSize = flag.Int("is", 64, "Icon Size") var iconSize = flag.Int("is", 64, "Icon Size")
@@ -166,7 +172,7 @@ var itemSpacing = flag.Uint("spacing", 20, "icon spacing")
var lang = flag.String("lang", "", "force lang, e.g. \"en\", \"pl\"") var lang = flag.String("lang", "", "force lang, e.g. \"en\", \"pl\"")
var fileManager = flag.String("fm", "thunar", "File Manager") var fileManager = flag.String("fm", "thunar", "File Manager")
var term = flag.String("term", defaultTermIfBlank(os.Getenv("TERM"), "foot"), "Terminal emulator") var term = flag.String("term", defaultTermIfBlank(os.Getenv("TERM"), "foot"), "Terminal emulator")
var wm = flag.String("wm", "", "use swaymsg exec (with 'sway' argument) or hyprctl dispatch exec (with 'hyprland') to launch programs") var wm = flag.String("wm", "", "use swaymsg exec (with 'sway' argument) or hyprctl dispatch exec (with 'hyprland') or riverctl spawn (with 'river') to launch programs")
var nameLimit = flag.Int("fslen", 80, "File Search name LENgth Limit") var nameLimit = flag.Int("fslen", 80, "File Search name LENgth Limit")
var noCats = flag.Bool("nocats", false, "Disable filtering by category") var noCats = flag.Bool("nocats", false, "Disable filtering by category")
var noFS = flag.Bool("nofs", false, "Disable file search") var noFS = flag.Bool("nofs", false, "Disable file search")
@@ -176,7 +182,8 @@ var pbLock = flag.String("pblock", "", "command for the Lock power bar icon")
var pbPoweroff = flag.String("pbpoweroff", "", "command for the Poweroff power bar icon") var pbPoweroff = flag.String("pbpoweroff", "", "command for the Poweroff power bar icon")
var pbReboot = flag.String("pbreboot", "", "command for the Reboot power bar icon") var pbReboot = flag.String("pbreboot", "", "command for the Reboot power bar icon")
var pbSleep = flag.String("pbsleep", "", "command for the sleep power bar icon") var pbSleep = flag.String("pbsleep", "", "command for the sleep power bar icon")
var pbSize = flag.Int("pbsize", 64, "power bar icon size") var pbSize = flag.Int("pbsize", 64, "power bar icon size (only works w/ built-in icons)")
var pbUseIconTheme = flag.Bool("pbuseicontheme", false, "use icon theme instead of built-in icons in power bar")
var debug = flag.Bool("d", false, "Turn on Debug messages") var debug = flag.Bool("d", false, "Turn on Debug messages")
func main() { func main() {
@@ -198,8 +205,10 @@ func main() {
// v0.2: we also need to support SIGUSR from now on // v0.2: we also need to support SIGUSR from now on
showWindowChannel := make(chan interface{}, 1) showWindowChannel := make(chan interface{}, 1)
signalChan := make(chan os.Signal, 1) signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGUSR1) const (
SIG25 = syscall.Signal(0x25) // Which is SIGRTMIN+3 on Linux, it's not used by the system
)
signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGUSR1, syscall.SIGUSR2, SIG25)
go func() { go func() {
for { for {
s := <-signalChan s := <-signalChan
@@ -207,7 +216,7 @@ func main() {
case syscall.SIGTERM: case syscall.SIGTERM:
log.Info("SIGTERM received, bye bye") log.Info("SIGTERM received, bye bye")
gtk.MainQuit() gtk.MainQuit()
case syscall.SIGUSR1: case syscall.SIGUSR1: // toggle drawer
if *resident { if *resident {
// As win.Show() called from inside a goroutine randomly crashes GTK, // As win.Show() called from inside a goroutine randomly crashes GTK,
// let's just set e helper variable here. We'll be checking it with glib.TimeoutAdd. // let's just set e helper variable here. We'll be checking it with glib.TimeoutAdd.
@@ -222,6 +231,23 @@ func main() {
log.Info("SIGUSR1 received, and I'm not resident, bye bye") log.Info("SIGUSR1 received, and I'm not resident, bye bye")
gtk.MainQuit() gtk.MainQuit()
} }
case syscall.SIGUSR2: // open drawer
if *resident {
log.Debug("SIGUSR2 received, showing the window")
showWindowChannel <- struct{}{}
} else {
log.Info("SIGUSR2 received, and I'm not resident but I'm still here, doing nothing")
}
case SIG25: // close drawer
if *resident {
log.Debug("SIG25 received, hiding the window")
if win.IsVisible() {
restoreStateAndHide()
}
} else {
log.Info("A signal received, and I'm not resident, bye bye")
gtk.MainQuit()
}
default: default:
log.Infof("Unknown signal: %s", s.String()) log.Infof("Unknown signal: %s", s.String())
} }
@@ -230,10 +256,10 @@ func main() {
// If running instance found, we want it to show the window. The new instance will send SIGUSR1 and die // If running instance found, we want it to show the window. The new instance will send SIGUSR1 and die
// (equivalent of `pkill -USR1 nwg-drawer`). // (equivalent of `pkill -USR1 nwg-drawer`).
// Otherwise the command may behave in two ways: // Otherwise, the command may behave in two ways:
// 1. kill the running non-residennt instance and exit; // 1. kill the running non-resident instance and exit;
// 2. die if a resident instance found. // 2. die if a resident instance found.
lockFilePath := path.Join(dataDir(), "nwg-drawer.lock") lockFilePath := path.Join(dataHome(), "nwg-drawer.lock")
lockFile, err := singleinstance.CreateLockFile(lockFilePath) lockFile, err := singleinstance.CreateLockFile(lockFilePath)
if err != nil { if err != nil {
pid, err := readTextFile(lockFilePath) pid, err := readTextFile(lockFilePath)
@@ -243,8 +269,17 @@ func main() {
if *resident { if *resident {
log.Warnf("Resident instance already running (PID %v)", i) log.Warnf("Resident instance already running (PID %v)", i)
} else { } else {
log.Infof("Showing resident instance (PID %v)", i) var err error
err := syscall.Kill(i, syscall.SIGUSR1) if *flagDrawerClose {
log.Infof("Closing resident instance (PID %v)", i)
err = syscall.Kill(i, SIG25)
} else if *flagDrawerOpen {
log.Infof("Showing resident instance (PID %v)", i)
err = syscall.Kill(i, syscall.SIGUSR2)
} else {
log.Infof("Toggling resident instance (PID %v)", i)
err = syscall.Kill(i, syscall.SIGUSR1)
}
if err != nil { if err != nil {
return return
} }
@@ -265,6 +300,7 @@ func main() {
// ENVIRONMENT // ENVIRONMENT
configDirectory = configDir() configDirectory = configDir()
dataDirectory = dataDir()
// Placing the drawer config files in the nwg-panel config directory was a mistake. // Placing the drawer config files in the nwg-panel config directory was a mistake.
// Let's move them to their own location. // Let's move them to their own location.
@@ -290,7 +326,7 @@ func main() {
// Copy default style sheet if not found // Copy default style sheet if not found
if !pathExists(filepath.Join(configDirectory, "drawer.css")) { if !pathExists(filepath.Join(configDirectory, "drawer.css")) {
err := copyFile("/usr/share/nwg-drawer/drawer.css", filepath.Join(configDirectory, "drawer.css")) err := copyFile(filepath.Join(dataDirectory, "drawer.css"), filepath.Join(configDirectory, "drawer.css"))
if err != nil { if err != nil {
log.Errorf("Failed copying 'drawer.css' file: %s", err) log.Errorf("Failed copying 'drawer.css' file: %s", err)
} }
@@ -384,7 +420,6 @@ func main() {
err = cssProvider.LoadFromPath(*cssFileName) err = cssProvider.LoadFromPath(*cssFileName)
if err != nil { if err != nil {
log.Errorf("ERROR: %s css file not found or erroneous. Using GTK styling.", *cssFileName) log.Errorf("ERROR: %s css file not found or erroneous. Using GTK styling.", *cssFileName)
log.Errorf("%s", err)
} else { } else {
log.Info(fmt.Sprintf("Using style from %s", *cssFileName)) log.Info(fmt.Sprintf("Using style from %s", *cssFileName))
screen, _ := gdk.ScreenGetDefault() screen, _ := gdk.ScreenGetDefault()
@@ -398,12 +433,13 @@ func main() {
if wayland() { if wayland() {
layershell.InitForWindow(win) layershell.InitForWindow(win)
layershell.SetNamespace(win, "nwg-drawer")
var output2mon map[string]*gdk.Monitor var output2mon map[string]*gdk.Monitor
if *targetOutput != "" { if *targetOutput != "" {
// We want to assign layershell to a monitor, but we only know the output name! // We want to assign layershell to a monitor, but we only know the output name!
output2mon, err = mapOutputs() output2mon, err = mapOutputs()
fmt.Println(">>>", output2mon) log.Debugf("output2mon: %s", output2mon)
if err == nil { if err == nil {
monitor := output2mon[*targetOutput] monitor := output2mon[*targetOutput]
layershell.SetMonitor(win, monitor) layershell.SetMonitor(win, monitor)
@@ -459,8 +495,26 @@ func main() {
} }
} }
return true return true
} else if key.KeyVal() == gdk.KEY_Tab {
if firstPowerBtn != nil {
firstPowerBtn.ToWidget().GrabFocus()
}
} else if key.KeyVal() == gdk.KEY_Return {
s, _ := searchEntry.GetText()
if s != "" {
// Check if the search box content is an arithmetic expression. If so, display the result
// and copy to the clipboard with wl-copy.
result, err := expr.Eval(s, nil)
if err == nil {
log.Debugf("Setting up mathemathical operation result window. Operation: %s, result: %v", s, result)
setUpOperationResultWindow(s, fmt.Sprintf("%v", result))
}
}
return true
} }
return false return true
}) })
win.Connect("key-press-event", func(_ *gtk.Window, event *gdk.Event) bool { win.Connect("key-press-event", func(_ *gtk.Window, event *gdk.Event) bool {
@@ -474,8 +528,8 @@ func main() {
if !searchEntry.IsFocus() { if !searchEntry.IsFocus() {
searchEntry.GrabFocusWithoutSelecting() searchEntry.GrabFocusWithoutSelecting()
} }
return false
} }
return false
}) })
/* /*
@@ -518,6 +572,18 @@ func main() {
resultWindow.SetEvents(int(gdk.ALL_EVENTS_MASK)) resultWindow.SetEvents(int(gdk.ALL_EVENTS_MASK))
resultWindow.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) resultWindow.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
// On touch screen we don't want the button-release-event to launch the app if the user just wanted to scroll the
// window. Let's forbid doing so if the content has been scrolled. We will reset the value on button-press-event.
// Resolves https://github.com/nwg-piotr/nwg-drawer/issues/110
vAdj := resultWindow.GetVAdjustment()
vAdj.Connect("value-changed", func() {
beenScrolled = true
})
hAdj := resultWindow.GetHAdjustment()
hAdj.Connect("value-changed", func() {
beenScrolled = true
})
resultWindow.Connect("button-release-event", func(_ *gtk.ScrolledWindow, event *gdk.Event) bool { resultWindow.Connect("button-release-event", func(_ *gtk.ScrolledWindow, event *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(event) btnEvent := gdk.EventButtonNewFromEvent(event)
if btnEvent.Button() == 3 { if btnEvent.Button() == 3 {
@@ -566,32 +632,66 @@ func main() {
} }
// Power Button Bar // Power Button Bar
if *pbExit != "" || *pbLock != "" || *pbPoweroff != "" || *pbReboot != "" || *pbSleep != "" { if dataDirectory != "" {
powerBarWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) if *pbExit != "" || *pbLock != "" || *pbPoweroff != "" || *pbReboot != "" || *pbSleep != "" {
outerVBox.PackStart(powerBarWrapper, false, false, 0) powerBarWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
powerButtonsWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) outerVBox.PackStart(powerBarWrapper, false, false, 0)
powerBarWrapper.PackStart(powerButtonsWrapper, true, false, 12) powerButtonsWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
powerBarWrapper.PackStart(powerButtonsWrapper, true, false, 12)
if *pbLock != "" { if *pbPoweroff != "" {
btn := powerButton("/usr/share/nwg-drawer/img/lock.svg", *pbLock) btn, _ := gtk.ButtonNew()
powerButtonsWrapper.PackStart(btn, true, false, 0) if !*pbUseIconTheme {
} btn = powerButton(filepath.Join(dataDirectory, "img/poweroff.svg"), *pbPoweroff)
if *pbExit != "" { } else {
btn := powerButton("/usr/share/nwg-drawer/img/exit.svg", *pbExit) btn = powerButton("system-shutdown-symbolic", *pbPoweroff)
powerButtonsWrapper.PackStart(btn, true, false, 0) }
} powerButtonsWrapper.PackEnd(btn, true, false, 0)
if *pbReboot != "" { firstPowerBtn = btn
btn := powerButton("/usr/share/nwg-drawer/img/reboot.svg", *pbReboot) }
powerButtonsWrapper.PackStart(btn, true, false, 0) if *pbSleep != "" {
} btn, _ := gtk.ButtonNew()
if *pbSleep != "" { if !*pbUseIconTheme {
btn := powerButton("/usr/share/nwg-drawer/img/sleep.svg", *pbSleep) btn = powerButton(filepath.Join(dataDirectory, "img/sleep.svg"), *pbSleep)
powerButtonsWrapper.PackStart(btn, true, false, 0) } else {
} btn = powerButton("face-yawn-symbolic", *pbSleep)
if *pbPoweroff != "" { }
btn := powerButton("/usr/share/nwg-drawer/img/poweroff.svg", *pbPoweroff) powerButtonsWrapper.PackEnd(btn, true, false, 0)
powerButtonsWrapper.PackStart(btn, true, false, 0) firstPowerBtn = btn
}
if *pbReboot != "" {
btn, _ := gtk.ButtonNew()
if !*pbUseIconTheme {
btn = powerButton(filepath.Join(dataDirectory, "img/reboot.svg"), *pbReboot)
} else {
btn = powerButton("system-reboot-symbolic", *pbReboot)
}
powerButtonsWrapper.PackEnd(btn, true, false, 0)
firstPowerBtn = btn
}
if *pbExit != "" {
btn, _ := gtk.ButtonNew()
if !*pbUseIconTheme {
btn = powerButton(filepath.Join(dataDirectory, "img/exit.svg"), *pbExit)
} else {
btn = powerButton("system-log-out-symbolic", *pbExit)
}
powerButtonsWrapper.PackEnd(btn, true, false, 0)
firstPowerBtn = btn
}
if *pbLock != "" {
btn, _ := gtk.ButtonNew()
if !*pbUseIconTheme {
btn = powerButton(filepath.Join(dataDirectory, "img/lock.svg"), *pbLock)
} else {
btn = powerButton("system-lock-screen-symbolic", *pbLock)
}
powerButtonsWrapper.PackEnd(btn, true, false, 0)
firstPowerBtn = btn
}
} }
} else {
log.Warn("Couldn't find data dir, power bar icons unavailable")
} }
statusLineWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) statusLineWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)

View File

@@ -173,7 +173,7 @@ func configHome() string {
return path.Join(os.Getenv("HOME"), ".config") return path.Join(os.Getenv("HOME"), ".config")
} }
func dataDir() string { func dataHome() string {
var dir string var dir string
if xdgData := os.Getenv("XDG_DATA_HOME"); xdgData != "" { if xdgData := os.Getenv("XDG_DATA_HOME"); xdgData != "" {
dir = path.Join(xdgData, "nwg-drawer") dir = path.Join(xdgData, "nwg-drawer")
@@ -181,7 +181,7 @@ func dataDir() string {
dir = path.Join(home, ".local/share/nwg-drawer") dir = path.Join(home, ".local/share/nwg-drawer")
} }
log.Infof("Data dir: %s", dir) log.Debugf("Data home: %s", dir)
createDir(dir) createDir(dir)
return dir return dir
@@ -197,7 +197,7 @@ func createDir(dir string) {
} }
func copyFile(src, dst string) error { func copyFile(src, dst string) error {
log.Infof("Copying file: %s", dst) log.Infof("Copying: '%s' => '%s'", src, dst)
var err error var err error
var srcfd *os.File var srcfd *os.File
@@ -233,6 +233,22 @@ func copyFile(src, dst string) error {
return os.Chmod(dst, srcinfo.Mode()) return os.Chmod(dst, srcinfo.Mode())
} }
func dataDir() string {
xdgDataDirs := os.Getenv("XDG_DATA_DIRS")
if xdgDataDirs == "" {
xdgDataDirs = "/usr/local/share/:/usr/share/"
}
for _, d := range strings.Split(xdgDataDirs, ":") {
p := filepath.Join(d, "nwg-drawer")
if pathExists(p) {
log.Infof("Data dir: %v", p)
return p
}
}
log.Warnf("Data dir not found")
return ""
}
func getAppDirs() []string { func getAppDirs() []string {
var dirs []string var dirs []string
@@ -321,9 +337,10 @@ func listDesktopFiles() []string {
func setUpCategories() { func setUpCategories() {
var other category var other category
dDir := dataDir()
for _, cName := range categoryNames { for _, cName := range categoryNames {
fileName := fmt.Sprintf("%s.directory", cName) fileName := fmt.Sprintf("%s.directory", cName)
fp := filepath.Join("/usr/share/nwg-drawer/desktop-directories", fileName) fp := filepath.Join(dDir, "desktop-directories", fileName)
lines, err := loadTextFile(fp) lines, err := loadTextFile(fp)
if err == nil { if err == nil {
var cat category var cat category
@@ -554,7 +571,7 @@ func savePinned() {
} }
} }
func launch(command string, terminal bool) { func launch(command string, terminal bool, terminate bool) {
// trim % and everything afterwards // trim % and everything afterwards
if strings.Contains(command, "%") { if strings.Contains(command, "%") {
cutAt := strings.Index(command, "%") cutAt := strings.Index(command, "%")
@@ -594,9 +611,11 @@ func launch(command string, terminal bool) {
cmd = exec.Command("swaymsg", "exec", strings.Join(elements, " ")) cmd = exec.Command("swaymsg", "exec", strings.Join(elements, " "))
} else if *wm == "hyprland" || *wm == "Hyprland" { } else if *wm == "hyprland" || *wm == "Hyprland" {
cmd = exec.Command("hyprctl", "dispatch", "exec", strings.Join(elements, " ")) cmd = exec.Command("hyprctl", "dispatch", "exec", strings.Join(elements, " "))
} else if *wm == "river" {
cmd = exec.Command("riverctl", "spawn", strings.Join(elements, " "))
} }
msg := fmt.Sprintf("command: %q; args: %q\n", cmd.Args[0], cmd.Args[1:]) msg := fmt.Sprintf("Executing command: %q; args: %q\n", cmd.Args[0], cmd.Args[1:])
log.Info(msg) log.Info(msg)
cmd.SysProcAttr = &syscall.SysProcAttr{ cmd.SysProcAttr = &syscall.SysProcAttr{
@@ -613,10 +632,12 @@ func launch(command string, terminal bool) {
}() }()
} }
if *resident { if terminate {
restoreStateAndHide() if *resident {
} else { restoreStateAndHide()
gtk.MainQuit() } else {
gtk.MainQuit()
}
} }
} }
@@ -670,13 +691,8 @@ func mapOutputs() (map[string]*gdk.Monitor, error) {
num := display.GetNMonitors() num := display.GetNMonitors()
for i := 0; i < num; i++ { for i := 0; i < num; i++ {
mon, _ := display.GetMonitor(i) mon, _ := display.GetMonitor(i)
geometry := mon.GetGeometry() output := hyprlandMonitors[i]
// assign output to monitor on the basis of the same x, y coordinates result[output.Name] = mon
for _, output := range hyprlandMonitors {
if int(output.X) == geometry.GetX() && int(output.Y) == geometry.GetY() {
result[output.Name] = mon
}
}
} }
} else { } else {
return nil, err return nil, err
@@ -704,13 +720,8 @@ func mapOutputs() (map[string]*gdk.Monitor, error) {
num := display.GetNMonitors() num := display.GetNMonitors()
for i := 0; i < num; i++ { for i := 0; i < num; i++ {
mon, _ := display.GetMonitor(i) mon, _ := display.GetMonitor(i)
geometry := mon.GetGeometry() output := outputs[i]
// assign output to monitor on the basis of the same x, y coordinates result[output.Name] = mon
for _, output := range outputs {
if int(output.Rect.X) == geometry.GetX() && int(output.Rect.Y) == geometry.GetY() {
result[output.Name] = mon
}
}
} }
} else { } else {
return nil, errors.New("output assignment only supported on sway and Hyprland") return nil, errors.New("output assignment only supported on sway and Hyprland")
@@ -737,7 +748,15 @@ func substring(s string, start int, end int) string {
func hyprctl(cmd string) ([]byte, error) { func hyprctl(cmd string) ([]byte, error) {
his := os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") his := os.Getenv("HYPRLAND_INSTANCE_SIGNATURE")
socketFile := fmt.Sprintf("/tmp/hypr/%s/.socket.sock", his) xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR")
hyprDir := ""
if xdgRuntimeDir != "" {
hyprDir = fmt.Sprintf("%s/hypr", xdgRuntimeDir)
} else {
hyprDir = "/tmp/hypr"
}
socketFile := fmt.Sprintf("%s/%s/.socket.sock", hyprDir, his)
conn, err := net.Dial("unix", socketFile) conn, err := net.Dial("unix", socketFile)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"github.com/dlasky/gotk3-layershell/layershell"
"io/fs" "io/fs"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -66,7 +67,7 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
btn.Connect("button-release-event", func(row *gtk.Button, e *gdk.Event) bool { btn.Connect("button-release-event", func(row *gtk.Button, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e) btnEvent := gdk.EventButtonNewFromEvent(e)
if btnEvent.Button() == 1 { if btnEvent.Button() == 1 {
launch(entry.Exec, entry.Terminal) launch(entry.Exec, entry.Terminal, true)
return true return true
} else if btnEvent.Button() == 3 { } else if btnEvent.Button() == 3 {
unpinItem(entry.DesktopID) unpinItem(entry.DesktopID)
@@ -75,7 +76,7 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
return false return false
}) })
btn.Connect("activate", func() { btn.Connect("activate", func() {
launch(entry.Exec, entry.Terminal) launch(entry.Exec, entry.Terminal, true)
}) })
btn.Connect("enter-notify-event", func() { btn.Connect("enter-notify-event", func() {
statusLabel.SetText(entry.CommentLoc) statusLabel.SetText(entry.CommentLoc)
@@ -264,11 +265,19 @@ func flowBoxButton(entry desktopEntry) *gtk.Button {
r := substring(desc, 0, 117) r := substring(desc, 0, 117)
desc = fmt.Sprintf("%s…", string(r)) desc = fmt.Sprintf("%s…", string(r))
} }
button.Connect("button-press-event", func() {
// if not scrolled from now on, we will allow launching apps on button-release-event
beenScrolled = false
})
button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool { button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e) btnEvent := gdk.EventButtonNewFromEvent(e)
if btnEvent.Button() == 1 { if btnEvent.Button() == 1 {
launch(exec, terminal) if !beenScrolled {
return true launch(exec, terminal, true)
return true
}
} else if btnEvent.Button() == 3 { } else if btnEvent.Button() == 3 {
pinItem(ID) pinItem(ID)
return true return true
@@ -276,7 +285,7 @@ func flowBoxButton(entry desktopEntry) *gtk.Button {
return false return false
}) })
button.Connect("activate", func() { button.Connect("activate", func() {
launch(exec, terminal) launch(exec, terminal, true)
}) })
button.Connect("enter-notify-event", func() { button.Connect("enter-notify-event", func() {
statusLabel.SetText(desc) statusLabel.SetText(desc)
@@ -290,32 +299,37 @@ func flowBoxButton(entry desktopEntry) *gtk.Button {
return button return button
} }
func powerButton(iconPath, command string) *gtk.Button { func powerButton(iconPathOrName, command string) *gtk.Button {
button, _ := gtk.ButtonNew() button, _ := gtk.ButtonNew()
button.SetAlwaysShowImage(true) button.SetAlwaysShowImage(true)
var pixbuf *gdk.Pixbuf var pixbuf *gdk.Pixbuf
var img *gtk.Image var img *gtk.Image
var err error var err error
pixbuf, err = gdk.PixbufNewFromFileAtSize(iconPath, *pbSize, *pbSize) if !*pbUseIconTheme {
if err != nil { pixbuf, err = gdk.PixbufNewFromFileAtSize(iconPathOrName, *pbSize, *pbSize)
pixbuf, _ = createPixbuf("unknown", *pbSize) if err != nil {
log.Warnf("Couldn't find icon %s", iconPath) pixbuf, _ = createPixbuf("unknown", *pbSize)
log.Warnf("Couldn't find icon %s", iconPathOrName)
}
img, _ = gtk.ImageNewFromPixbuf(pixbuf)
} else {
img, _ = gtk.ImageNewFromIconName(iconPathOrName, gtk.ICON_SIZE_DIALOG)
} }
img, _ = gtk.ImageNewFromPixbuf(pixbuf)
button.SetImage(img) button.SetImage(img)
button.SetImagePosition(gtk.POS_TOP) button.SetImagePosition(gtk.POS_TOP)
button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool { button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e) btnEvent := gdk.EventButtonNewFromEvent(e)
if btnEvent.Button() == 1 { if btnEvent.Button() == 1 {
launch(command, false) launch(command, false, true)
return true return true
} }
return false return false
}) })
button.Connect("activate", func() { button.Connect("activate", func() {
launch(command, false) launch(command, false, true)
}) })
button.Connect("enter-notify-event", func() { button.Connect("enter-notify-event", func() {
statusLabel.SetText(command) statusLabel.SetText(command)
@@ -571,3 +585,54 @@ func setUpUserFileSearchResultButton(fileName, filePath string) *gtk.Box {
box.PackStart(button, false, true, 0) box.PackStart(button, false, true, 0)
return box return box
} }
func setUpOperationResultWindow(operation string, result string) {
win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
if err != nil {
log.Fatal("Unable to create result window:", err)
}
win.SetModal(true)
if wayland() {
layershell.InitForWindow(win)
layershell.SetLayer(win, layershell.LAYER_SHELL_LAYER_OVERLAY)
layershell.SetKeyboardMode(win, layershell.LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE)
}
// any key to close the window
win.Connect("key-release-event", func(_ *gtk.Window, event *gdk.Event) bool {
win.Destroy()
return true
})
// any button to close the window
win.Connect("button-release-event", func(_ *gtk.Window, event *gdk.Event) bool {
win.Destroy()
return true
})
outerVBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 6)
win.Add(outerVBox)
vBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 5)
outerVBox.PackStart(vBox, true, true, 6)
lbl, _ := gtk.LabelNew(fmt.Sprintf("%s = %s", operation, result))
lbl.SetProperty("name", "math-label")
vBox.PackStart(lbl, true, true, 12)
mRefProvider, _ := gtk.CssProviderNew()
css := "window { background-color: rgba (0, 0, 0, 255); color: #fff; border: solid 1px grey; border-radius: 5px}"
err = mRefProvider.LoadFromData(css)
if err != nil {
log.Warn(err)
}
ctx, _ := win.GetStyleContext()
ctx.AddProvider(mRefProvider, gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
win.ShowAll()
if wayland() {
cmd := fmt.Sprintf("wl-copy %v", result)
launch(cmd, false, false)
}
}