Skip to content

Commit

Permalink
Merge pull request #12 from igorkulman/feature/automatically-set-wall…
Browse files Browse the repository at this point in the history
…paper

Automatically set the wallpaper
  • Loading branch information
igorkulman authored Nov 21, 2020
2 parents 30360c1 + 706f4e5 commit 55841f1
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 60 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,3 @@ jobs:
- uses: actions/checkout@v2
- name: Build
run: swift build
- name: Run tests
run: swift test
9 changes: 9 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
{
"object": {
"pins": [
{
"package": "Files",
"repositoryURL": "https://github.com/JohnSundell/Files",
"state": {
"branch": null,
"revision": "d273b5b7025d386feef79ef6bad7de762e106eaf",
"version": "4.2.0"
}
},
{
"package": "Rainbow",
"repositoryURL": "https://github.com/onevcat/Rainbow",
Expand Down
4 changes: 3 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.3.1"),
.package(url: "https://github.com/thii/SwiftHEXColors.git", from: "1.3.1"),
.package(url: "https://github.com/onevcat/Rainbow", from: "3.0.0")
.package(url: "https://github.com/onevcat/Rainbow", from: "3.0.0"),
.package(url: "https://github.com/JohnSundell/Files", from: "4.0.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand All @@ -22,6 +23,7 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "SwiftHEXColors", package: "SwiftHEXColors"),
.product(name: "Rainbow", package: "Rainbow"),
.product(name: "Files", package: "Files")
]),
.testTarget(
name: "ChangeMenuBarColorTests",
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ and run
swift build -c release
```

To create a `ChangeMenuBarColor` executable file in `.build/release`.
to create a `ChangeMenuBarColor` executable file in `.build/release`.

## Usage

### Solid color

To create a new wallpaper file with a solid color rectangle that matches the menu bar, run
To set a new wallpaper file with a solid color rectangle that matches the menu bar, run

```swift
./ChangeMenuBarColor SolidColor "desired_hex_color" "optional_path_to_your_wallpaper"
Expand All @@ -89,7 +89,7 @@ the currently set wallpaper will be used.

### Gradient

To create a new wallpaper file with a gradient rectangle at the top, run
To set a new wallpaper file with a gradient rectangle at the top, run

```swift
./ChangeMenuBarColor Gradient "start_hex_color" "end_hex_color" "optional_path_to_your_wallpaper"
Expand All @@ -109,6 +109,10 @@ If you do not provide the wallpaper path

the currently set wallpaper will be used.

## Known issues

Dynamic wallpapers are not supported at the moment. If you use a dynamic wallpaper the utility will not be able to use it and will fail.

## Support the project

<a href="https://www.buymeacoffee.com/igorkulman" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
Expand Down
68 changes: 22 additions & 46 deletions Sources/ChangeMenuBarColor/Commands/Abstract/Command.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,58 +6,36 @@
//

import ArgumentParser
import Files
import Foundation
import Cocoa
import Rainbow
import SwiftHEXColors

class Command {
func createWallpaper(screenSize: CGSize, menuBarHeight: CGFloat) -> NSImage? {
func createWallpaper(screen: NSScreen) -> NSImage? {
return nil
}

func run() {
print("Starting up".green)
print("Found \(NSScreen.screens.count) screens\n")

var generatedImages: [String] = []
var index = 0

for screen in NSScreen.screens {
index = index+1

print("Processing screen \(index) of \(NSScreen.screens.count)")

let screenSize = screen.frame.size
let menuBarHeight = screenSize.height - screen.visibleFrame.height - screen.visibleFrame.origin.y
print("Processing screen \(screen.index) of \(NSScreen.screens.count)")

guard let adjustedWallpaper = createWallpaper(screenSize: screenSize, menuBarHeight: menuBarHeight), let data = adjustedWallpaper.jpgData else {
print("Could not generate new wallpaper for screen \(index)".red)
guard let adjustedWallpaper = createWallpaper(screen: screen), let data = adjustedWallpaper.jpgData else {
print("Could not generate new wallpaper for screen \(screen.index)".red)
continue
}

let workingDirectory = FileManager.default.currentDirectoryPath
let adjustedWallpaperFile = workingDirectory.appending("/wallpaper-screen\(index)-adjusted.jpg")

do {
try data.write(to: URL(fileURLWithPath: adjustedWallpaperFile))
generatedImages.append(adjustedWallpaperFile)
print("Created new wallpaper for screen \(index)".blue)
} catch {
print("Writing new wallpaper file failed with \(error.localizedDescription) for screen \(index)".red)
}
print("\n")
setWallpaper(screen: screen, wallpaper: data)
}

print("All done!".green)
print("Here is the list of generated wallpaper images:".green)
for image in generatedImages {
print("\(image)\n".blue)
}
print("Do not forget to set the generated wallpaper images as your desktop background!".yellow)
}

func loadWallpaperImage(wallpaper: String?) -> NSImage? {
func loadWallpaperImage(wallpaper: String?, screen: NSScreen) -> NSImage? {
if let path = wallpaper {
guard let wallpaper = NSImage(contentsOfFile: path) else {
print("Cannot read the provided wallpaper file as image. Check if the path is correct and if it is a valid image file".red)
Expand All @@ -68,7 +46,7 @@ class Command {
return wallpaper
}

guard let path = getCurrentWallpaperPath(), let wallpaper = NSImage(contentsOfFile: path) else {
guard let path = NSWorkspace.shared.desktopImageURL(for: screen), let wallpaper = NSImage(contentsOf: path) else {
print("Cannot read the currently set macOS wallpaper".red)
print("Try providing a specific wallpaper as a parameter instead".blue)
return nil
Expand All @@ -79,26 +57,24 @@ class Command {
return wallpaper
}

private func getCurrentWallpaperPath() -> String? {
let task = Process()
task.executableURL = URL(fileURLWithPath: "/usr/bin/sqlite3")
task.arguments = ["-readonly",
FileManager.default.homeDirectoryForCurrentUser.path.appending("/Library/Application Support/Dock/desktoppicture.db"),
"SELECT * FROM data ORDER BY rowID DESC LIMIT 1;"
]
private func setWallpaper(screen: NSScreen, wallpaper: Data) {
guard let supportFiles = try? Folder.library?.subfolder(at: "Application Support"), let workingDirectory = try? supportFiles.createSubfolderIfNeeded(at: "ChangeMenuBarColor") else {
print("Cannot access Application Support folder".red)
return
}

let outputPipe = Pipe()
do {
let generatedWallpaperFile = workingDirectory.url.appendingPathComponent("/wallpaper-screen\(screen.index)-adjusted-\(UUID().uuidString).jpg")
try? FileManager.default.removeItem(at: generatedWallpaperFile)

task.standardOutput = outputPipe
try wallpaper.write(to: generatedWallpaperFile)
print("Created new wallpaper for screen \(screen.index) in \(generatedWallpaperFile.absoluteString)")

do {
try task.run()
try NSWorkspace.shared.setDesktopImageURL(generatedWallpaperFile, for: screen, options: [:])
print("Wallpper set".blue)
} catch {
print("Trying to get the currenty set macOS wallpaper failed with \(error)")
print("Writing new wallpaper file failed with \(error.localizedDescription) for screen \(screen.index)".red)
}

let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
let output = String(decoding: outputData, as: UTF8.self).trimmingCharacters(in: .whitespacesAndNewlines)
return NSString(string: output).expandingTildeInPath
print("\n")
}
}
8 changes: 4 additions & 4 deletions Sources/ChangeMenuBarColor/Commands/Gradient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ final class Gradient: Command, ParsableCommand {
@Argument(help: "Wallpaper to use. If not provided the current macOS wallpaper will be used")
private var wallpaper: String?

override func createWallpaper(screenSize: CGSize, menuBarHeight: CGFloat) -> NSImage? {
guard let wallpaper = loadWallpaperImage(wallpaper: wallpaper) else {
override func createWallpaper(screen: NSScreen) -> NSImage? {
guard let wallpaper = loadWallpaperImage(wallpaper: wallpaper, screen: screen) else {
return nil
}

Expand All @@ -41,13 +41,13 @@ final class Gradient: Command, ParsableCommand {
return nil
}

guard let resizedWallpaper = wallpaper.resized(to: screenSize) else {
guard let resizedWallpaper = wallpaper.resized(to: screen.size) else {
print("Cannot not resize provided wallpaper to screen size".red)
return nil
}

print("Generating gradient image")
guard let topImage = createGradientImage(startColor: startColor, endColor: endColor, width: screenSize.width, height: menuBarHeight) else {
guard let topImage = createGradientImage(startColor: startColor, endColor: endColor, width: screen.size.width, height: screen.menuBarHeight) else {
return nil
}

Expand Down
8 changes: 4 additions & 4 deletions Sources/ChangeMenuBarColor/Commands/SolidColor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ final class SolidColor: Command, ParsableCommand {
@Argument(help: "Wallpaper to use. If not provided the current macOS wallpaper will be used")
private var wallpaper: String?

override func createWallpaper(screenSize: CGSize, menuBarHeight: CGFloat) -> NSImage? {
guard let wallpaper = loadWallpaperImage(wallpaper: wallpaper) else {
override func createWallpaper(screen: NSScreen) -> NSImage? {
guard let wallpaper = loadWallpaperImage(wallpaper: wallpaper, screen: screen) else {
return nil
}

Expand All @@ -33,13 +33,13 @@ final class SolidColor: Command, ParsableCommand {
return nil
}

guard let resizedWallpaper = wallpaper.resized(to: screenSize) else {
guard let resizedWallpaper = wallpaper.resized(to: screen.size) else {
print("Cannot not resize provided wallpaper to screen size".red)
return nil
}

print("Generating solid color image")
guard let topImage = createSolidImage(color: color, width: screenSize.width, height: menuBarHeight) else {
guard let topImage = createSolidImage(color: color, width: screen.size.width, height: screen.menuBarHeight) else {
return nil
}

Expand Down
23 changes: 23 additions & 0 deletions Sources/ChangeMenuBarColor/Extensions/NSScreen+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// File.swift
//
//
// Created by Igor Kulman on 21.11.2020.
//

import Foundation
import Cocoa

extension NSScreen {
var size: CGSize {
return frame.size
}

var menuBarHeight: CGFloat {
return size.height - visibleFrame.height - visibleFrame.origin.y
}

var index: Int {
(NSScreen.screens.firstIndex(of: self) ?? 0) + 1
}
}

0 comments on commit 55841f1

Please sign in to comment.