Skip to content

Commit a61cc8f

Browse files
feat: Implement window and full-screen screenshotting on Windows
This commit introduces significant new features and refactors the existing codebase. - Window-specific Screenshots: Users can now select a specific window to capture. - Enhanced Full-screen Capture: The full-screen screenshot functionality is improved with better multi-display handling. - Configuration System: A JSON configuration file is now used to manage settings, such as the save location. An option to open the config file is available via the --config flag. - FFmpeg Downloader: Prepares for future video recording functionality by adding a utility to prompt for and download FFmpeg. - Windows Toast Notifications: The application now displays a system notification when a screenshot is saved, with a button to open the file directly. - Code Refactoring: The project has been restructured with the addition of utils.go and a significant overhaul of dependencies, replacing the previous screenshot library with robotgo. - Project Rename: The Go module is renamed from go-screen-cli to captr.
1 parent c2aacfb commit a61cc8f

File tree

6 files changed

+806
-68
lines changed

6 files changed

+806
-68
lines changed

go.mod

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
1-
module go-screen-cli
1+
module captr
22

33
go 1.24.5
44

55
require (
6-
github.com/AllenDang/w32 v0.0.0-20180428130237-ad0a36d80adc // indirect
7-
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
8-
github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e // indirect
9-
github.com/ebitengine/purego v0.8.3 // indirect
6+
github.com/JamesHovious/w32 v1.2.0
7+
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4
8+
github.com/go-vgo/robotgo v0.110.8
9+
github.com/manifoldco/promptui v0.9.0
10+
github.com/schollz/progressbar/v3 v3.18.0
11+
github.com/tailscale/win v0.0.0-20250627215312-f4da2b8ee071
12+
golang.design/x/clipboard v0.7.1
13+
)
14+
15+
require (
16+
github.com/chzyer/readline v1.5.1 // indirect
17+
github.com/dblohm7/wingoes v0.0.0-20250611174154-e3e096948d18 // indirect
18+
github.com/ebitengine/purego v0.8.4 // indirect
1019
github.com/gen2brain/shm v0.1.1 // indirect
1120
github.com/go-ole/go-ole v1.3.0 // indirect
12-
github.com/go-vgo/robotgo v0.110.8 // indirect
1321
github.com/godbus/dbus/v5 v5.1.0 // indirect
1422
github.com/jezek/xgb v1.1.1 // indirect
15-
github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018 // indirect
1623
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
17-
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
18-
github.com/manifoldco/promptui v0.9.0 // indirect
24+
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
25+
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
1926
github.com/otiai10/gosseract v2.2.1+incompatible // indirect
27+
github.com/otiai10/mint v1.6.3 // indirect
2028
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
29+
github.com/rivo/uniseg v0.4.7 // indirect
2130
github.com/robotn/xgb v0.10.0 // indirect
2231
github.com/robotn/xgbutil v0.10.0 // indirect
23-
github.com/shirou/gopsutil/v4 v4.25.4 // indirect
24-
github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35 // indirect
32+
github.com/shirou/gopsutil/v4 v4.25.7 // indirect
2533
github.com/tklauser/go-sysconf v0.3.15 // indirect
2634
github.com/tklauser/numcpus v0.10.0 // indirect
2735
github.com/vcaesar/gops v0.41.0 // indirect
@@ -30,7 +38,11 @@ require (
3038
github.com/vcaesar/screenshot v0.11.1 // indirect
3139
github.com/vcaesar/tt v0.20.1 // indirect
3240
github.com/yusufpapurcu/wmi v1.2.4 // indirect
33-
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
34-
golang.org/x/image v0.27.0 // indirect
35-
golang.org/x/sys v0.33.0 // indirect
41+
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
42+
golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476 // indirect
43+
golang.org/x/image v0.29.0 // indirect
44+
golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f // indirect
45+
golang.org/x/net v0.42.0 // indirect
46+
golang.org/x/sys v0.34.0 // indirect
47+
golang.org/x/term v0.33.0 // indirect
3648
)

go.sum

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,76 @@
1-
github.com/AllenDang/w32 v0.0.0-20180428130237-ad0a36d80adc h1:w3fW4b1hPf6/cfdQQ/vu9V8eBeQmuLZIaUMj81nIzYQ=
2-
github.com/AllenDang/w32 v0.0.0-20180428130237-ad0a36d80adc/go.mod h1:1rHKulT5eD2DzdKxDXUZRKtBfkTzLmTL42ZmEmOfyrs=
31
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ=
42
github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0=
3+
github.com/JamesHovious/w32 v1.2.0 h1:UZWv2ea6y4Kylzs7DGXWcSCaUH+OxH8q7aCFmMUqwao=
4+
github.com/JamesHovious/w32 v1.2.0/go.mod h1:L6LTHMKvhtpGUkIYGTo4U9PKancYKpGS/+naYtTs8Ys=
5+
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
6+
github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
57
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
6-
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
8+
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
9+
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
710
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
11+
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
12+
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
813
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
9-
github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e h1:L+XrFvD0vBIBm+Wf9sFN6aU395t7JROoai0qXZraA4U=
10-
github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e/go.mod h1:SUxUaAK/0UG5lYyZR1L1nC4AaYYvSSYTWQSH3FPcxKU=
11-
github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc=
12-
github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
14+
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
15+
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
16+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
17+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
18+
github.com/dblohm7/wingoes v0.0.0-20250611174154-e3e096948d18 h1:1+ezXI2ZjiS8zenp08GFowF3zkVQ4j8/CPaALxqCBq0=
19+
github.com/dblohm7/wingoes v0.0.0-20250611174154-e3e096948d18/go.mod h1:SUxUaAK/0UG5lYyZR1L1nC4AaYYvSSYTWQSH3FPcxKU=
20+
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
21+
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
1322
github.com/gen2brain/shm v0.1.1 h1:1cTVA5qcsUFixnDHl14TmRoxgfWEEZlTezpUj1vm5uQ=
1423
github.com/gen2brain/shm v0.1.1/go.mod h1:UgIcVtvmOu+aCJpqJX7GOtiN7X2ct+TKLg4RTxwPIUA=
1524
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
1625
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
1726
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
27+
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE=
28+
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10=
1829
github.com/go-vgo/robotgo v0.110.8 h1:tWoUyqlZgDJ61bQju3WGSb/NIIfNV4TkYL3GFeWcHio=
1930
github.com/go-vgo/robotgo v0.110.8/go.mod h1:45w33PzprtFncpw4cAt9SzMtSY9XnVfotu+RrCVN8JE=
2031
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
2132
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
33+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
34+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
2235
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
2336
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
24-
github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018 h1:NQYgMY188uWrS+E/7xMVpydsI48PMHcc7SfR4OxkDF4=
25-
github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018/go.mod h1:Pmpz2BLf55auQZ67u3rvyI2vAQvNetkK/4zYUmpauZQ=
2637
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc=
2738
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
28-
github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc=
29-
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
3039
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
3140
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
41+
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
42+
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
43+
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
44+
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
45+
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
46+
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
47+
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
48+
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
3249
github.com/otiai10/gosseract v2.2.1+incompatible h1:Ry5ltVdpdp4LAa2bMjsSJH34XHVOV7XMi41HtzL8X2I=
3350
github.com/otiai10/gosseract v2.2.1+incompatible/go.mod h1:XrzWItCzCpFRZ35n3YtVTgq5bLAhFIkascoRo8G32QE=
51+
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
52+
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
53+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
54+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3455
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
3556
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
57+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
58+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
3659
github.com/robotn/xgb v0.0.0-20190912153532-2cb92d044934/go.mod h1:SxQhJskUJ4rleVU44YvnrdvxQr0tKy5SRSigBrCgyyQ=
3760
github.com/robotn/xgb v0.10.0 h1:O3kFbIwtwZ3pgLbp1h5slCQ4OpY8BdwugJLrUe6GPIM=
3861
github.com/robotn/xgb v0.10.0/go.mod h1:SxQhJskUJ4rleVU44YvnrdvxQr0tKy5SRSigBrCgyyQ=
3962
github.com/robotn/xgbutil v0.10.0 h1:gvf7mGQqCWQ68aHRtCxgdewRk+/KAJui6l3MJQQRCKw=
4063
github.com/robotn/xgbutil v0.10.0/go.mod h1:svkDXUDQjUiWzLrA0OZgHc4lbOts3C+uRfP6/yjwYnU=
41-
github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw=
42-
github.com/shirou/gopsutil/v4 v4.25.4/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
43-
github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35 h1:wAZbkTZkqDzWsqxPh2qkBd3KvFU7tcxV0BP0Rnhkxog=
44-
github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35/go.mod h1:aMd4yDHLjbOuYP6fMxj1d9ACDQlSWwYztcpybGHCQc8=
64+
github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA=
65+
github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
66+
github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM=
67+
github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U=
68+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
69+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
70+
github.com/tailscale/win v0.0.0-20250627215312-f4da2b8ee071 h1:qo7kOhoN5DHioXNlFytBzIoA5glW6lsb8YqV0lP3IyE=
71+
github.com/tailscale/win v0.0.0-20250627215312-f4da2b8ee071/go.mod h1:aMd4yDHLjbOuYP6fMxj1d9ACDQlSWwYztcpybGHCQc8=
72+
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
73+
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
4574
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
4675
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
4776
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
@@ -58,14 +87,26 @@ github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4=
5887
github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU=
5988
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
6089
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
61-
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
62-
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
63-
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
64-
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
90+
golang.design/x/clipboard v0.7.1 h1:OEG3CmcYRBNnRwpDp7+uWLiZi3hrMRJpE9JkkkYtz2c=
91+
golang.design/x/clipboard v0.7.1/go.mod h1:i5SiIqj0wLFw9P/1D7vfILFK0KHMk7ydE72HRrUIgkg=
92+
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4=
93+
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
94+
golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476 h1:Wdx0vgH5Wgsw+lF//LJKmWOJBLWX6nprsMqnf99rYDE=
95+
golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8=
96+
golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas=
97+
golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA=
98+
golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f h1:/n+PL2HlfqeSiDCuhdBbRNlGS/g2fM4OHufalHaTVG8=
99+
golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f/go.mod h1:ESkJ836Z6LpG6mTVAhA48LpfW/8fNR0ifStlH2axyfg=
100+
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
101+
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
65102
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
66103
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
67-
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
68104
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
105+
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
69106
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
70-
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
71-
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
107+
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
108+
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
109+
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
110+
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
111+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
112+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

Lines changed: 176 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,185 @@
11
package main
22

33
import (
4+
"archive/zip"
5+
"encoding/json"
6+
"flag"
47
"fmt"
5-
"image/png"
8+
"io"
9+
"net/http"
610
"os"
7-
"time"
11+
"os/exec"
12+
"path/filepath"
13+
"runtime"
14+
"strings"
815

916
"github.com/manifoldco/promptui"
10-
"github.com/vcaesar/screenshot"
17+
"github.com/schollz/progressbar/v3"
1118
)
1219

20+
var (
21+
config Config
22+
appdataDir string
23+
configFilePath string
24+
)
25+
26+
func extractFFmpegExe(zipPath, destDir string) error {
27+
r, err := zip.OpenReader(zipPath)
28+
if err != nil {
29+
return err
30+
}
31+
defer r.Close()
32+
33+
for _, f := range r.File {
34+
if strings.HasSuffix(f.Name, "bin/ffmpeg.exe") || strings.HasSuffix(f.Name, "bin\\ffmpeg.exe") {
35+
rc, err := f.Open()
36+
if err != nil {
37+
return err
38+
}
39+
defer rc.Close()
40+
41+
os.MkdirAll(destDir, 0755)
42+
outPath := filepath.Join(destDir, "ffmpeg.exe")
43+
outFile, err := os.Create(outPath)
44+
if err != nil {
45+
return err
46+
}
47+
defer outFile.Close()
48+
49+
_, err = io.Copy(outFile, rc)
50+
return err
51+
}
52+
}
53+
54+
return os.ErrNotExist
55+
}
56+
57+
type Config struct {
58+
SaveLocation string `json:"save_location"`
59+
RecordFunc bool `json:"record_func_enabled"`
60+
}
1361

62+
func setConfig(key string, value any) {
63+
data := make(map[string]any)
64+
content, err := os.ReadFile(configFilePath)
65+
if err == nil {
66+
_ = json.Unmarshal(content, &data)
67+
}
68+
69+
data[key] = value
70+
71+
updData, err := json.MarshalIndent(data, "", " ")
72+
if err != nil {
73+
panic(err)
74+
}
75+
os.WriteFile(configFilePath, updData, 0644)
76+
initConfig()
77+
}
78+
79+
func initConfig() {
80+
var err error
81+
appdataDir, err = os.UserConfigDir()
82+
if err != nil {
83+
panic(err)
84+
}
85+
appdataDir = filepath.Join(appdataDir, "captr")
86+
configFilePath = filepath.Join(appdataDir, ".captr_config.json")
87+
if _, err := os.Stat(configFilePath); os.IsNotExist(err) {
88+
home, _ := os.UserHomeDir()
89+
config = Config{
90+
SaveLocation: filepath.Join(home, "Desktop"),
91+
RecordFunc: true,
92+
}
93+
data, err := json.MarshalIndent(config, "", " ")
94+
if err != nil {
95+
panic(err)
96+
}
97+
os.Mkdir(appdataDir, 0755)
98+
os.WriteFile(configFilePath, data, 0644)
99+
} else {
100+
data, err := os.ReadFile(configFilePath)
101+
if err != nil {
102+
panic(err)
103+
}
104+
if err := json.Unmarshal(data, &config); err != nil {
105+
panic(err)
106+
}
107+
}
108+
}
109+
110+
func initDownloads() {
111+
dwnPath := filepath.Join(appdataDir, "bin")
112+
if _, err := os.Stat(filepath.Join(dwnPath, "ffmpeg.exe")); err == nil {
113+
return
114+
}
115+
if !config.RecordFunc {
116+
return
117+
}
118+
cmd := exec.Command("ffmpeg", "-version")
119+
if err := cmd.Run(); err == nil {
120+
return
121+
}
122+
fmt.Println("Captr requires ffmpeg to record videos. However, the screenshotting functionality is not affected.")
123+
prompt := promptui.Select{
124+
Label: "Choose your action",
125+
Items: []string{
126+
"Download ffmpeg (Download size: ~148MB, Install size: ~132MB)",
127+
"Keep only screenshotting functionality",
128+
},
129+
}
130+
i, _, err := prompt.Run()
131+
if err != nil {
132+
fmt.Println("Action Cancelled")
133+
os.Exit(1)
134+
}
135+
if i == 0 {
136+
resp, err := http.Get("https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-n7.1-latest-win64-gpl-7.1.zip")
137+
if err != nil {
138+
fmt.Println("Couldn't download ffmpeg")
139+
os.Exit(1)
140+
}
141+
defer resp.Body.Close()
142+
bar := progressbar.DefaultBytes(
143+
resp.ContentLength,
144+
"Downloading ffmpeg",
145+
)
146+
147+
out, err := os.Create(filepath.Join(os.TempDir(), "ffmpeg_captr.zip"))
148+
if err != nil {
149+
fmt.Println("Couldn't download ffmpeg")
150+
os.Exit(1)
151+
}
152+
defer out.Close()
153+
154+
_, err = io.Copy(io.MultiWriter(out, bar), resp.Body)
155+
if err != nil {
156+
fmt.Printf("Couldn't download ffmpeg.")
157+
os.Exit(1)
158+
}
159+
extractFFmpegExe(filepath.Join(os.TempDir(), "ffmpeg_captr.zip"), dwnPath)
160+
fmt.Printf("FFMPEG has been downloaded to %s", dwnPath)
161+
} else {
162+
setConfig("record_func_enabled", false)
163+
}
164+
}
165+
166+
func init() {
167+
if !(runtime.GOOS == "windows" && runtime.GOARCH == "amd64") {
168+
panic("Captr is only supported on Windows x64")
169+
}
170+
initConfig()
171+
configMode := flag.Bool("config", false, "Configure Captr")
172+
flag.Parse()
173+
if *configMode {
174+
cmd := exec.Command("notepad.exe", configFilePath)
175+
if err := cmd.Start(); err != nil {
176+
fmt.Println("Error starting command:", err)
177+
return
178+
}
179+
os.Exit(0)
180+
}
181+
initDownloads()
182+
}
14183

15184
func main() {
16185
fmt.Println(`
@@ -25,7 +194,7 @@ ________/\\\\\\\\\__________________________________________________________
25194
_______\/////////___\////////\//__\///______________\/////____\///__________
26195
27196
`)
28-
197+
fmt.Println("Open config file by passing the --config flag")
29198
capture_ops := []string{"Record full screen", "Record specific window", "Screenshot specific window", "Screenshot full screen"}
30199
prompt := promptui.Select{
31200
Label: "Select Action",
@@ -38,8 +207,10 @@ ________/\\\\\\\\\__________________________________________________________
38207
fmt.Print("Action Cancelled.")
39208
return
40209
}
41-
210+
42211
switch i {
212+
case 2:
213+
Screenshot_Window()
43214
case 3:
44215
Screenshot_Display()
45216
}

0 commit comments

Comments
 (0)